{
  "openapi": "3.1.0",
  "info": {
    "title": "Oplac REST API",
    "version": "2026-10-03",
    "summary": "Płatności online, linki do płatności, strony sprzedaży bez sklepu, portal klienta, zwroty, klienci, subskrypcje, faktury i zdarzenia dla polskiego biznesu.",
    "description": "REST API Oplac v1. Oplac jest oprogramowaniem działającym na koncie dostawcy płatności sprzedawcy: środki przyjmuje i rozlicza dostawca, Oplac daje jedno API, hostowaną stronę płatności, webhooki, subskrypcje, faktury i panel.\n\n## Pierwsza płatność testowa w kilka minut\n1. Załóż konto i w panelu, w zakładce Deweloperzy, wygeneruj klucz testowy sk_test_ (dostępny od razu, także w piaskownicy po zapisie).\n2. `POST /v1/payments` z `amount`, `currency` i `return_url`. W odpowiedzi jest `checkout_url`.\n3. Otwórz `checkout_url` i zapłać kodem BLIK 777 123 (albo wywołaj `POST /v1/test_helpers/payments/{id}/confirm`).\n4. Klient wraca na `return_url?payment=pay_...`; potwierdź status przez `GET /v1/payments/{id}`.\n5. Zapisz adres webhooka (`POST /v1/webhook_endpoints`) i oznaczaj zamówienia po zdarzeniu `payment.succeeded`.\n6. Gdy konto ma plan i podpiętego dostawcę, wygeneruj klucz sk_live_ i podmień go. Reszta integracji zostaje bez zmian.\n\n## Bez programisty\nFirma bez sklepu sprzedaje przez stronę sprzedaży: `POST /v1/products` (produkt albo subskrypcja), `POST /v1/checkout_pages` (adres, kod QR, przycisk HTML), a klient końcowy zarządza zakupami w portalu klienta (`POST /v1/portal_sessions`). To samo robi się bez API w panelu, w grupie Sprzedaż.\n\n## Uwierzytelnianie\nNagłówek `Authorization: Bearer sk_test_...` (piaskownica) albo `sk_live_...` (produkcja). Tryb wynika z klucza; obiekty testowe i produkcyjne są rozdzielone.\n\n## Konwencje\nKwoty w groszach (najmniejszej jednostce waluty). Daty jako znaczniki czasu Unix w sekundach (`created`, `expires_at`, `paid_at`, `current_period_end`), wyjątek: `due_date` i `issue_date` faktur jako RRRR-MM-DD. Każdy obiekt ma `id` ze stałym przedrostkiem (pay_, plink_, re_, cus_, plan_, coupon_, sub_, inv_, prod_, cpage_, bps_, evt_, we_, acct_), `object`, `livemode`, `created` i `metadata`.\n\n## Listy\nKażda lista to `{object: \"list\", url, has_more, data}` z obiektami od najnowszego. Stronicowanie kursorem: `limit` (1 do 100), `starting_after` (starsze), `ending_before` (nowsze), filtr `created[gt|gte|lt|lte]`.\n\n## Rozwijanie\n`expand[]=customer` zamienia identyfikator w pełny obiekt (na liście `expand[]=data.customer`). Dozwolone pola są wymienione przy każdej trasie; zagnieżdżanie kropką: `expand[]=payment.customer`.\n\n## Idempotencja\nNagłówek `Idempotency-Key` (do 255 znaków, np. UUID) przy POST i DELETE. Powtórka z tym samym kluczem i tą samą treścią w ciągu 24 godzin zwraca zapisany wynik z nagłówkami `Idempotent-Replayed: true` i `Original-Request`. Ten sam klucz z inną treścią albo inną trasą: 400 `idempotency_key_reused`. Błędy 400, 401 i 429 oraz 5xx nie są zapamiętywane, więc poprawione żądanie może iść z tym samym kluczem. Dwa równoległe żądania z jednym kluczem są wykonywane po kolei.\n\n## Limity zapytań\n100 żądań na sekundę w produkcji, 25 w piaskownicy, na konto i tryb. Nagłówki `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`; po przekroczeniu 429 `rate_limited` z `Retry-After`.\n\n## Błędy\nKażdy błąd (także 404 nieznanej trasy, 405, 429, 500) to JSON `{error: {type, code, message, param, doc_url, request_id}}`. Katalog kodów w `x-oplac-error-codes`. `X-Request-Id` jest na każdej odpowiedzi; podaj go w zgłoszeniu.\n\n## Webhooki\nZdarzenia idą jako POST z JSON obiektu `event` i nagłówkiem `Oplac-Signature: t=<unix>,v1=<hex>`, gdzie v1 to HMAC SHA-256 (sekret whsec_) z ciągu `<t>.<surowa treść>`. Sprawdź podpis stałym czasem i odrzuć znacznik starszy niż 5 minut (ochrona przed powtórką). Odpowiedz 2xx w ciągu 10 sekund; inaczej ponowienia po 1 min, 5 min, 30 min, 2 h, 6 h, 12 h, potem co 24 h, łącznie 3 doby. To samo zdarzenie może przyjść więcej niż raz: obsługuj je idempotentnie po `id`.\n\n## Wersja\nNagłówek `Oplac-Version` (dziś jedyna wersja: 2026-10-03). Zmiany zgodne wstecz (nowe pola, nowe typy zdarzeń) nie zmieniają wersji; zmiana niekompatybilna dostanie nową datę, a stara zostanie dostępna przez nagłówek.",
    "termsOfService": "https://oplac.pl/regulamin",
    "contact": {
      "name": "Oplac",
      "url": "https://oplac.pl/docs",
      "email": "app@oplac.pl"
    }
  },
  "servers": [
    {
      "url": "https://oplac.pl/api",
      "description": "Produkcja i piaskownica (tryb z klucza)."
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Konto",
      "description": "Stan konta i modułów dla klucza."
    },
    {
      "name": "Płatności",
      "description": "Płatność z hostowaną stroną płatności, adresem powrotu i webhookiem."
    },
    {
      "name": "Linki do płatności",
      "description": "Stałe adresy do zapłaty, z kodem QR."
    },
    {
      "name": "Zwroty",
      "description": "Zwroty całościowe i częściowe."
    },
    {
      "name": "Klienci",
      "description": "Klienci sprzedawcy (cus_)."
    },
    {
      "name": "Plany",
      "description": "Plany abonamentowe (moduł Bill)."
    },
    {
      "name": "Kupony",
      "description": "Rabaty do subskrypcji."
    },
    {
      "name": "Subskrypcje",
      "description": "Płatności cykliczne z okresem próbnym, proracją i Recover."
    },
    {
      "name": "Faktury",
      "description": "Faktury VAT z terminem, po opłaceniu i korekty, KSeF."
    },
    {
      "name": "Produkty",
      "description": "Produkty do sprzedaży bez sklepu (Bez programisty)."
    },
    {
      "name": "Strony sprzedaży",
      "description": "Publiczne strony zakupu w marce sprzedawcy, z kodem QR i przyciskiem."
    },
    {
      "name": "Portal klienta",
      "description": "Jednorazowe adresy wejścia do portalu klienta końcowego."
    },
    {
      "name": "Zdarzenia",
      "description": "Historia zdarzeń i ponowna wysyłka."
    },
    {
      "name": "Webhooki",
      "description": "Adres webhooka i sekret podpisu."
    },
    {
      "name": "Piaskownica",
      "description": "Pomocnicy testowi dla klucza sk_test_."
    }
  ],
  "paths": {
    "/v1/account": {
      "get": {
        "tags": [
          "Konto"
        ],
        "summary": "Pobierz konto",
        "description": "Stan konta dla klucza: plan, moduły, waluty, czy produkcja przyjmuje płatności (charges_enabled), czy jest dostawca i webhook. Dobre pierwsze wywołanie po wygenerowaniu klucza.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          }
        ],
        "operationId": "pobierz_konto",
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Account"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/payments": {
      "post": {
        "tags": [
          "Płatności"
        ],
        "summary": "Utwórz płatność",
        "description": "Wymagane tylko amount i currency. Odpowiedź ma checkout_url (hostowana strona płatności). Przepływ dla sklepu: utwórz płatność z return_url i metadata (numer zamówienia), przekieruj klienta na checkout_url, po powrocie potwierdź status przez GET /v1/payments/{id}, a zamówienie oznaczaj po zdarzeniu payment.succeeded z webhooka. Piaskownica: strona płatności ma kody BLIK 777 123 (sukces), 777 000 (odmowa), 777 408 (zły kod) i przyciski wyniku karty; wynik można też ustawić przez POST /v1/test_helpers/payments/{id}/confirm.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "operationId": "utw_rz_p_atno",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "amount": {
                    "type": "integer",
                    "minimum": 100,
                    "maximum": 999999999,
                    "description": "Kwota w groszach (najmniejszej jednostce waluty): 12900 to 129,00 zł. Od 100 (1,00)."
                  },
                  "currency": {
                    "type": "string",
                    "enum": [
                      "PLN",
                      "EUR",
                      "USD"
                    ],
                    "description": "Waluta. EUR i USD od planu Skala."
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 255,
                    "description": "Opis widoczny dla klienta na stronie płatności."
                  },
                  "return_url": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 500,
                    "description": "Adres powrotu po zapłacie; klient wraca tam z parametrem ?payment=pay_... Stan płatności zawsze potwierdź przez GET /v1/payments/{id} albo webhook, nie z adresu."
                  },
                  "customer": {
                    "oneOf": [
                      {
                        "type": "string",
                        "description": "Identyfikator klienta cus_."
                      },
                      {
                        "type": "object",
                        "properties": {
                          "email": {
                            "type": "string",
                            "format": "email",
                            "maxLength": 190
                          },
                          "name": {
                            "type": "string",
                            "maxLength": 190
                          },
                          "tax_id": {
                            "type": "string",
                            "maxLength": 20,
                            "description": "NIP."
                          },
                          "address": {
                            "oneOf": [
                              {
                                "type": "object",
                                "properties": {
                                  "line1": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  },
                                  "line2": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  },
                                  "city": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  },
                                  "postal_code": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  },
                                  "country": {
                                    "type": [
                                      "string",
                                      "null"
                                    ],
                                    "description": "Kod kraju ISO 3166-1 alpha-2, np. PL."
                                  }
                                }
                              },
                              {
                                "type": "string"
                              }
                            ],
                            "description": "Obiekt (line1, line2, city, postal_code, country) albo jeden tekst."
                          },
                          "metadata": {
                            "type": "object",
                            "description": "Własne pary klucz: wartość (do 50 kluczy, klucz do 40 znaków, wartość tekstowa do 500 znaków). Wracają w obiekcie i w zdarzeniach webhooka. Przy zmianie nowe klucze dochodzą do zapisanych, pusta wartość usuwa klucz, pusty tekst zamiast obiektu czyści wszystko.",
                            "additionalProperties": {
                              "type": "string",
                              "maxLength": 500
                            },
                            "maxProperties": 50,
                            "propertyNames": {
                              "maxLength": 40
                            },
                            "examples": [
                              {
                                "order_id": "10482"
                              }
                            ]
                          }
                        },
                        "description": "Dane nowego klienta; klient powstaje razem z płatnością."
                      }
                    ]
                  },
                  "customer_email": {
                    "type": "string",
                    "format": "email"
                  },
                  "customer_name": {
                    "type": "string"
                  },
                  "metadata": {
                    "type": "object",
                    "description": "Własne pary klucz: wartość (do 50 kluczy, klucz do 40 znaków, wartość tekstowa do 500 znaków). Wracają w obiekcie i w zdarzeniach webhooka. Przy zmianie nowe klucze dochodzą do zapisanych, pusta wartość usuwa klucz, pusty tekst zamiast obiektu czyści wszystko.",
                    "additionalProperties": {
                      "type": "string",
                      "maxLength": 500
                    },
                    "maxProperties": 50,
                    "propertyNames": {
                      "maxLength": 40
                    },
                    "examples": [
                      {
                        "order_id": "10482"
                      }
                    ]
                  },
                  "expires_at": {
                    "type": "integer",
                    "description": "Do kiedy strona płatności przyjmuje zapłatę, znacznik czasu Unix w przyszłości. Domyślnie 24 godziny."
                  },
                  "connected_account": {
                    "type": "string",
                    "description": "Konto sprzedawcy platformy (Connect, plan Platforma), które dostaje płatność po potrąceniu prowizji."
                  },
                  "application_fee": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Prowizja platformy w groszach (wymaga connected_account)."
                  }
                },
                "required": [
                  "amount",
                  "currency"
                ]
              },
              "example": {
                "amount": 12900,
                "currency": "PLN",
                "description": "Zamówienie 10482",
                "return_url": "https://sklep.example.com/powrot",
                "metadata": {
                  "order_id": "10482"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Utworzono.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Payment"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "provider_not_connected: konto bez dostawcy płatności (tylko produkcja).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "currency_not_in_plan, connect_not_in_plan.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Płatności"
        ],
        "summary": "Lista płatności",
        "description": "Płatności konta w trybie klucza, od najnowszej.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 10
            },
            "description": "Liczba obiektów na stronie (1 do 100)."
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator obiektu; zwraca starsze od niego (następna strona)."
          },
          {
            "name": "ending_before",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator obiektu; zwraca nowsze od niego (poprzednia strona). Nie łączyć ze starting_after."
          },
          {
            "name": "created",
            "in": "query",
            "required": false,
            "style": "deepObject",
            "explode": true,
            "schema": {
              "type": "object",
              "properties": {
                "gt": {
                  "type": "integer"
                },
                "gte": {
                  "type": "integer"
                },
                "lt": {
                  "type": "integer"
                },
                "lte": {
                  "type": "integer"
                }
              }
            },
            "description": "Filtr po chwili utworzenia (Unix): created[gte]=1760000000."
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "requires_confirmation",
                "processing",
                "succeeded",
                "failed",
                "canceled"
              ]
            },
            "description": "Filtr statusu."
          },
          {
            "name": "customer",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Płatności klienta cus_."
          },
          {
            "name": "payment_link",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Płatności z linku plink_."
          },
          {
            "name": "subscription",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Płatności subskrypcji sub_."
          },
          {
            "name": "invoice",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Płatności faktury inv_."
          },
          {
            "name": "expand[]",
            "in": "query",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "customer",
                  "payment_link",
                  "subscription",
                  "invoice"
                ]
              }
            },
            "description": "Pola do rozwinięcia w pełny obiekt (na liście z przedrostkiem data. albo bez). Rozwijalne: customer, payment_link, subscription, invoice."
          }
        ],
        "operationId": "lista_p_atno_ci",
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "url",
                    "has_more",
                    "data"
                  ],
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list",
                      "description": "Nazwa typu obiektu, zawsze \"list\"."
                    },
                    "url": {
                      "type": "string",
                      "examples": [
                        "/v1/payments"
                      ]
                    },
                    "has_more": {
                      "type": "boolean",
                      "description": "true, gdy w tym kierunku są kolejne obiekty (następna strona: starting_after z id ostatniego obiektu)."
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Payment"
                      },
                      "description": "Obiekty od najnowszego."
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/payments/{id}": {
      "get": {
        "tags": [
          "Płatności"
        ],
        "summary": "Pobierz płatność",
        "description": "Aktualny stan płatności. W produkcji stan jest odczytywany od dostawcy płatności przy każdym wywołaniu, więc to wiarygodne źródło po powrocie klienta.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator pay_."
          },
          {
            "name": "expand[]",
            "in": "query",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "customer",
                  "payment_link",
                  "subscription",
                  "invoice"
                ]
              }
            },
            "description": "Pola do rozwinięcia w pełny obiekt (na liście z przedrostkiem data. albo bez). Rozwijalne: customer, payment_link, subscription, invoice."
          }
        ],
        "operationId": "pobierz_p_atno",
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Payment"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Płatności"
        ],
        "summary": "Zmień płatność",
        "description": "Zmiana description i metadata. Kwota, waluta i status płatności są stałe.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator pay_."
          }
        ],
        "operationId": "zmie_p_atno",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "description": {
                    "type": "string",
                    "maxLength": 255
                  },
                  "metadata": {
                    "type": "object",
                    "description": "Własne pary klucz: wartość (do 50 kluczy, klucz do 40 znaków, wartość tekstowa do 500 znaków). Wracają w obiekcie i w zdarzeniach webhooka. Przy zmianie nowe klucze dochodzą do zapisanych, pusta wartość usuwa klucz, pusty tekst zamiast obiektu czyści wszystko.",
                    "additionalProperties": {
                      "type": "string",
                      "maxLength": 500
                    },
                    "maxProperties": 50,
                    "propertyNames": {
                      "maxLength": 40
                    },
                    "examples": [
                      {
                        "order_id": "10482"
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Payment"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/payments/{id}/cancel": {
      "post": {
        "tags": [
          "Płatności"
        ],
        "summary": "Anuluj płatność",
        "description": "Zamyka stronę płatności (w produkcji także u dostawcy). Tylko status pending albo requires_confirmation; opłaconą płatność zwraca się przez POST /v1/refunds.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator pay_."
          }
        ],
        "operationId": "anuluj_p_atno",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {}
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Payment"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "payment_not_cancelable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "provider_error: dostawca nie zamknął strony, płatność zostaje otwarta; ponów.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/payment_links": {
      "post": {
        "tags": [
          "Linki do płatności"
        ],
        "summary": "Utwórz link do płatności",
        "description": "Stały adres do wysłania klientowi albo wydruku jako kod QR. Bez amount klient sam wpisuje kwotę. Każde opłacenie tworzy osobną płatność ze zdarzeniami payment.*.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "operationId": "utw_rz_link_do_p_atno_ci",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "amount": {
                    "type": "integer",
                    "minimum": 100,
                    "maximum": 999999999,
                    "description": "Kwota w groszach; pomiń, żeby klient wpisał kwotę sam."
                  },
                  "currency": {
                    "type": "string",
                    "enum": [
                      "PLN",
                      "EUR",
                      "USD"
                    ],
                    "description": "Waluta. EUR i USD od planu Skala."
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 255
                  },
                  "reusable": {
                    "type": "boolean",
                    "default": false,
                    "description": "true: link przyjmuje wiele płatności."
                  },
                  "collect_tax_id": {
                    "type": "boolean",
                    "default": false,
                    "description": "Strona pyta o NIP."
                  },
                  "expires_at": {
                    "type": "integer",
                    "description": "Do kiedy strona płatności przyjmuje zapłatę, znacznik czasu Unix w przyszłości. Domyślnie 24 godziny."
                  },
                  "metadata": {
                    "type": "object",
                    "description": "Własne pary klucz: wartość (do 50 kluczy, klucz do 40 znaków, wartość tekstowa do 500 znaków). Wracają w obiekcie i w zdarzeniach webhooka. Przy zmianie nowe klucze dochodzą do zapisanych, pusta wartość usuwa klucz, pusty tekst zamiast obiektu czyści wszystko.",
                    "additionalProperties": {
                      "type": "string",
                      "maxLength": 500
                    },
                    "maxProperties": 50,
                    "propertyNames": {
                      "maxLength": 40
                    },
                    "examples": [
                      {
                        "order_id": "10482"
                      }
                    ]
                  },
                  "connected_account": {
                    "type": "string",
                    "description": "Konto sprzedawcy platformy (Connect, plan Platforma), które dostaje płatność po potrąceniu prowizji."
                  },
                  "application_fee": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Prowizja platformy w groszach (wymaga connected_account)."
                  }
                },
                "required": [
                  "currency"
                ]
              },
              "example": {
                "amount": 12900,
                "currency": "PLN",
                "description": "Zamówienie 10482"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Utworzono.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentLink"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "provider_not_connected (produkcja).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "currency_not_in_plan, sandbox_limit_reached, connect_not_in_plan.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Linki do płatności"
        ],
        "summary": "Lista linków",
        "description": "Linki konta w trybie klucza.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 10
            },
            "description": "Liczba obiektów na stronie (1 do 100)."
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator obiektu; zwraca starsze od niego (następna strona)."
          },
          {
            "name": "ending_before",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator obiektu; zwraca nowsze od niego (poprzednia strona). Nie łączyć ze starting_after."
          },
          {
            "name": "created",
            "in": "query",
            "required": false,
            "style": "deepObject",
            "explode": true,
            "schema": {
              "type": "object",
              "properties": {
                "gt": {
                  "type": "integer"
                },
                "gte": {
                  "type": "integer"
                },
                "lt": {
                  "type": "integer"
                },
                "lte": {
                  "type": "integer"
                }
              }
            },
            "description": "Filtr po chwili utworzenia (Unix): created[gte]=1760000000."
          },
          {
            "name": "active",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Tylko włączone (true) albo wyłączone (false)."
          }
        ],
        "operationId": "lista_link_w",
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "url",
                    "has_more",
                    "data"
                  ],
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list",
                      "description": "Nazwa typu obiektu, zawsze \"list\"."
                    },
                    "url": {
                      "type": "string",
                      "examples": [
                        "/v1/payment_links"
                      ]
                    },
                    "has_more": {
                      "type": "boolean",
                      "description": "true, gdy w tym kierunku są kolejne obiekty (następna strona: starting_after z id ostatniego obiektu)."
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PaymentLink"
                      },
                      "description": "Obiekty od najnowszego."
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/payment_links/{id}": {
      "get": {
        "tags": [
          "Linki do płatności"
        ],
        "summary": "Pobierz link",
        "description": "Link po identyfikatorze.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator plink_."
          }
        ],
        "operationId": "pobierz_link",
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentLink"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Linki do płatności"
        ],
        "summary": "Zmień link",
        "description": "active (wyłączenie i ponowne włączenie), description, metadata.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator plink_."
          }
        ],
        "operationId": "zmie_link",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "active": {
                    "type": "boolean"
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 255
                  },
                  "metadata": {
                    "type": "object",
                    "description": "Własne pary klucz: wartość (do 50 kluczy, klucz do 40 znaków, wartość tekstowa do 500 znaków). Wracają w obiekcie i w zdarzeniach webhooka. Przy zmianie nowe klucze dochodzą do zapisanych, pusta wartość usuwa klucz, pusty tekst zamiast obiektu czyści wszystko.",
                    "additionalProperties": {
                      "type": "string",
                      "maxLength": 500
                    },
                    "maxProperties": 50,
                    "propertyNames": {
                      "maxLength": 40
                    },
                    "examples": [
                      {
                        "order_id": "10482"
                      }
                    ]
                  }
                }
              },
              "example": {
                "active": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentLink"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/refunds": {
      "post": {
        "tags": [
          "Zwroty"
        ],
        "summary": "Utwórz zwrot",
        "description": "Zwrot całej pozostałej kwoty (bez amount) albo części. Zwroty częściowe sumują się do kwoty płatności. Przy udanym zwrocie powstaje faktura korygująca, jeśli do płatności była faktura. Zdarzenia: refund.created, potem refund.succeeded albo refund.failed.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "operationId": "utw_rz_zwrot",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "payment": {
                    "type": "string",
                    "description": "Identyfikator płatności pay_ w statusie succeeded."
                  },
                  "amount": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "Kwota zwrotu w groszach; domyślnie cała pozostała kwota."
                  },
                  "reason": {
                    "type": "string",
                    "enum": [
                      "requested_by_customer",
                      "duplicate",
                      "fraudulent",
                      "other"
                    ]
                  },
                  "metadata": {
                    "type": "object",
                    "description": "Własne pary klucz: wartość (do 50 kluczy, klucz do 40 znaków, wartość tekstowa do 500 znaków). Wracają w obiekcie i w zdarzeniach webhooka. Przy zmianie nowe klucze dochodzą do zapisanych, pusta wartość usuwa klucz, pusty tekst zamiast obiektu czyści wszystko.",
                    "additionalProperties": {
                      "type": "string",
                      "maxLength": 500
                    },
                    "maxProperties": 50,
                    "propertyNames": {
                      "maxLength": 40
                    },
                    "examples": [
                      {
                        "order_id": "10482"
                      }
                    ]
                  }
                },
                "required": [
                  "payment"
                ]
              },
              "example": {
                "payment": "pay_Ab12Cd34Ef56Gh",
                "amount": 2900,
                "reason": "requested_by_customer"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Utworzono.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Refund"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing (payment).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "payment_not_refundable, refund_exceeds_amount.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "provider_error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Zwroty"
        ],
        "summary": "Lista zwrotów",
        "description": "Zwroty konta w trybie klucza.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 10
            },
            "description": "Liczba obiektów na stronie (1 do 100)."
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator obiektu; zwraca starsze od niego (następna strona)."
          },
          {
            "name": "ending_before",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator obiektu; zwraca nowsze od niego (poprzednia strona). Nie łączyć ze starting_after."
          },
          {
            "name": "created",
            "in": "query",
            "required": false,
            "style": "deepObject",
            "explode": true,
            "schema": {
              "type": "object",
              "properties": {
                "gt": {
                  "type": "integer"
                },
                "gte": {
                  "type": "integer"
                },
                "lt": {
                  "type": "integer"
                },
                "lte": {
                  "type": "integer"
                }
              }
            },
            "description": "Filtr po chwili utworzenia (Unix): created[gte]=1760000000."
          },
          {
            "name": "payment",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Zwroty jednej płatności pay_."
          },
          {
            "name": "expand[]",
            "in": "query",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "payment",
                  "payment.customer"
                ]
              }
            },
            "description": "Pola do rozwinięcia w pełny obiekt (na liście z przedrostkiem data. albo bez). Rozwijalne: payment, payment.customer."
          }
        ],
        "operationId": "lista_zwrot_w",
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "url",
                    "has_more",
                    "data"
                  ],
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list",
                      "description": "Nazwa typu obiektu, zawsze \"list\"."
                    },
                    "url": {
                      "type": "string",
                      "examples": [
                        "/v1/refunds"
                      ]
                    },
                    "has_more": {
                      "type": "boolean",
                      "description": "true, gdy w tym kierunku są kolejne obiekty (następna strona: starting_after z id ostatniego obiektu)."
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Refund"
                      },
                      "description": "Obiekty od najnowszego."
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/refunds/{id}": {
      "get": {
        "tags": [
          "Zwroty"
        ],
        "summary": "Pobierz zwrot",
        "description": "Zwrot po identyfikatorze; zwrot pending w produkcji jest odświeżany u dostawcy.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator re_."
          },
          {
            "name": "expand[]",
            "in": "query",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "payment",
                  "payment.customer"
                ]
              }
            },
            "description": "Pola do rozwinięcia w pełny obiekt (na liście z przedrostkiem data. albo bez). Rozwijalne: payment, payment.customer."
          }
        ],
        "operationId": "pobierz_zwrot",
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Refund"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Zwroty"
        ],
        "summary": "Zmień zwrot",
        "description": "Tylko metadata.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator re_."
          }
        ],
        "operationId": "zmie_zwrot",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "metadata": {
                    "type": "object",
                    "description": "Własne pary klucz: wartość (do 50 kluczy, klucz do 40 znaków, wartość tekstowa do 500 znaków). Wracają w obiekcie i w zdarzeniach webhooka. Przy zmianie nowe klucze dochodzą do zapisanych, pusta wartość usuwa klucz, pusty tekst zamiast obiektu czyści wszystko.",
                    "additionalProperties": {
                      "type": "string",
                      "maxLength": 500
                    },
                    "maxProperties": 50,
                    "propertyNames": {
                      "maxLength": 40
                    },
                    "examples": [
                      {
                        "order_id": "10482"
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Refund"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/customers": {
      "post": {
        "tags": [
          "Klienci"
        ],
        "summary": "Utwórz klienta",
        "description": "Wszystkie pola opcjonalne. Zdarzenie customer.created.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "operationId": "utw_rz_klienta",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 190
                  },
                  "name": {
                    "type": "string",
                    "maxLength": 190
                  },
                  "tax_id": {
                    "type": "string",
                    "maxLength": 20,
                    "description": "NIP."
                  },
                  "address": {
                    "oneOf": [
                      {
                        "type": "object",
                        "properties": {
                          "line1": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "line2": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "city": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "postal_code": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "country": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "Kod kraju ISO 3166-1 alpha-2, np. PL."
                          }
                        }
                      },
                      {
                        "type": "string"
                      }
                    ],
                    "description": "Obiekt (line1, line2, city, postal_code, country) albo jeden tekst."
                  },
                  "metadata": {
                    "type": "object",
                    "description": "Własne pary klucz: wartość (do 50 kluczy, klucz do 40 znaków, wartość tekstowa do 500 znaków). Wracają w obiekcie i w zdarzeniach webhooka. Przy zmianie nowe klucze dochodzą do zapisanych, pusta wartość usuwa klucz, pusty tekst zamiast obiektu czyści wszystko.",
                    "additionalProperties": {
                      "type": "string",
                      "maxLength": 500
                    },
                    "maxProperties": 50,
                    "propertyNames": {
                      "maxLength": 40
                    },
                    "examples": [
                      {
                        "order_id": "10482"
                      }
                    ]
                  }
                }
              },
              "example": {
                "email": "anna@example.com",
                "name": "Anna Nowak",
                "metadata": {
                  "crm_id": "7"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Utworzono.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Klienci"
        ],
        "summary": "Lista klientów",
        "description": "Klienci konta w trybie klucza, bez usuniętych.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 10
            },
            "description": "Liczba obiektów na stronie (1 do 100)."
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator obiektu; zwraca starsze od niego (następna strona)."
          },
          {
            "name": "ending_before",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator obiektu; zwraca nowsze od niego (poprzednia strona). Nie łączyć ze starting_after."
          },
          {
            "name": "created",
            "in": "query",
            "required": false,
            "style": "deepObject",
            "explode": true,
            "schema": {
              "type": "object",
              "properties": {
                "gt": {
                  "type": "integer"
                },
                "gte": {
                  "type": "integer"
                },
                "lt": {
                  "type": "integer"
                },
                "lte": {
                  "type": "integer"
                }
              }
            },
            "description": "Filtr po chwili utworzenia (Unix): created[gte]=1760000000."
          },
          {
            "name": "email",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Dokładny adres e-mail (bez względu na wielkość liter)."
          }
        ],
        "operationId": "lista_klient_w",
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "url",
                    "has_more",
                    "data"
                  ],
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list",
                      "description": "Nazwa typu obiektu, zawsze \"list\"."
                    },
                    "url": {
                      "type": "string",
                      "examples": [
                        "/v1/customers"
                      ]
                    },
                    "has_more": {
                      "type": "boolean",
                      "description": "true, gdy w tym kierunku są kolejne obiekty (następna strona: starting_after z id ostatniego obiektu)."
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Customer"
                      },
                      "description": "Obiekty od najnowszego."
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/customers/{id}": {
      "get": {
        "tags": [
          "Klienci"
        ],
        "summary": "Pobierz klienta",
        "description": "Klient po identyfikatorze; usunięty wraca jako {id, object, deleted: true}.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator cus_."
          }
        ],
        "operationId": "pobierz_klienta",
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Klienci"
        ],
        "summary": "Zmień klienta",
        "description": "Zmiana podanych pól; metadata łączone z zapisanymi. Zdarzenie customer.updated.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator cus_."
          }
        ],
        "operationId": "zmie_klienta",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 190
                  },
                  "name": {
                    "type": "string",
                    "maxLength": 190
                  },
                  "tax_id": {
                    "type": "string",
                    "maxLength": 20,
                    "description": "NIP."
                  },
                  "address": {
                    "oneOf": [
                      {
                        "type": "object",
                        "properties": {
                          "line1": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "line2": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "city": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "postal_code": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "country": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "Kod kraju ISO 3166-1 alpha-2, np. PL."
                          }
                        }
                      },
                      {
                        "type": "string"
                      }
                    ],
                    "description": "Obiekt (line1, line2, city, postal_code, country) albo jeden tekst."
                  },
                  "metadata": {
                    "type": "object",
                    "description": "Własne pary klucz: wartość (do 50 kluczy, klucz do 40 znaków, wartość tekstowa do 500 znaków). Wracają w obiekcie i w zdarzeniach webhooka. Przy zmianie nowe klucze dochodzą do zapisanych, pusta wartość usuwa klucz, pusty tekst zamiast obiektu czyści wszystko.",
                    "additionalProperties": {
                      "type": "string",
                      "maxLength": 500
                    },
                    "maxProperties": 50,
                    "propertyNames": {
                      "maxLength": 40
                    },
                    "examples": [
                      {
                        "order_id": "10482"
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "resource_deleted: klient usunięty.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Klienci"
        ],
        "summary": "Usuń klienta",
        "description": "Klient znika z listy i jego dane kontaktowe nie wracają w API; płatności i subskrypcje zachowują odwołanie cus_. Zdarzenie customer.deleted. Powtórne usunięcie też zwraca 200.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator cus_."
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "operationId": "usu_klienta",
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deleted"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/plans": {
      "post": {
        "tags": [
          "Plany"
        ],
        "summary": "Utwórz plan",
        "description": "Plan abonamentowy dla klientów sprzedawcy. Kwota, waluta i okres są stałe po utworzeniu (subskrypcje liczą się z nich).",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "operationId": "utw_rz_plan",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 120
                  },
                  "amount": {
                    "type": "integer",
                    "minimum": 100,
                    "maximum": 999999999,
                    "description": "Kwota w groszach (najmniejszej jednostce waluty): 12900 to 129,00 zł. Od 100 (1,00)."
                  },
                  "currency": {
                    "type": "string",
                    "enum": [
                      "PLN",
                      "EUR",
                      "USD"
                    ],
                    "description": "Waluta. EUR i USD od planu Skala."
                  },
                  "interval": {
                    "type": "string",
                    "enum": [
                      "month",
                      "year"
                    ]
                  },
                  "trial_days": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 365,
                    "default": 0
                  },
                  "metadata": {
                    "type": "object",
                    "description": "Własne pary klucz: wartość (do 50 kluczy, klucz do 40 znaków, wartość tekstowa do 500 znaków). Wracają w obiekcie i w zdarzeniach webhooka. Przy zmianie nowe klucze dochodzą do zapisanych, pusta wartość usuwa klucz, pusty tekst zamiast obiektu czyści wszystko.",
                    "additionalProperties": {
                      "type": "string",
                      "maxLength": 500
                    },
                    "maxProperties": 50,
                    "propertyNames": {
                      "maxLength": 40
                    },
                    "examples": [
                      {
                        "order_id": "10482"
                      }
                    ]
                  }
                },
                "required": [
                  "name",
                  "amount",
                  "currency",
                  "interval"
                ]
              },
              "example": {
                "name": "Pro miesięcznie",
                "amount": 4900,
                "currency": "PLN",
                "interval": "month",
                "trial_days": 14
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Utworzono.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Plan"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "provider_not_connected (produkcja).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "module_not_in_plan: moduł Bill jest od planu Firma.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Plany"
        ],
        "summary": "Lista planów",
        "description": "Plany konta w trybie klucza.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 10
            },
            "description": "Liczba obiektów na stronie (1 do 100)."
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator obiektu; zwraca starsze od niego (następna strona)."
          },
          {
            "name": "ending_before",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator obiektu; zwraca nowsze od niego (poprzednia strona). Nie łączyć ze starting_after."
          },
          {
            "name": "created",
            "in": "query",
            "required": false,
            "style": "deepObject",
            "explode": true,
            "schema": {
              "type": "object",
              "properties": {
                "gt": {
                  "type": "integer"
                },
                "gte": {
                  "type": "integer"
                },
                "lt": {
                  "type": "integer"
                },
                "lte": {
                  "type": "integer"
                }
              }
            },
            "description": "Filtr po chwili utworzenia (Unix): created[gte]=1760000000."
          },
          {
            "name": "active",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Tylko włączone albo wyłączone."
          }
        ],
        "operationId": "lista_plan_w",
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "url",
                    "has_more",
                    "data"
                  ],
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list",
                      "description": "Nazwa typu obiektu, zawsze \"list\"."
                    },
                    "url": {
                      "type": "string",
                      "examples": [
                        "/v1/plans"
                      ]
                    },
                    "has_more": {
                      "type": "boolean",
                      "description": "true, gdy w tym kierunku są kolejne obiekty (następna strona: starting_after z id ostatniego obiektu)."
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Plan"
                      },
                      "description": "Obiekty od najnowszego."
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "module_not_in_plan: moduł Bill jest od planu Firma.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/plans/{id}": {
      "get": {
        "tags": [
          "Plany"
        ],
        "summary": "Pobierz plan",
        "description": "Plan po identyfikatorze.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator plan_."
          }
        ],
        "operationId": "pobierz_plan",
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Plan"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "module_not_in_plan: moduł Bill jest od planu Firma.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Plany"
        ],
        "summary": "Zmień plan",
        "description": "name, active (wyłączony plan nie przyjmuje nowych subskrypcji), metadata. Próba zmiany amount, currency albo interval to 400 parameter_invalid.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator plan_."
          }
        ],
        "operationId": "zmie_plan",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 120
                  },
                  "active": {
                    "type": "boolean"
                  },
                  "metadata": {
                    "type": "object",
                    "description": "Własne pary klucz: wartość (do 50 kluczy, klucz do 40 znaków, wartość tekstowa do 500 znaków). Wracają w obiekcie i w zdarzeniach webhooka. Przy zmianie nowe klucze dochodzą do zapisanych, pusta wartość usuwa klucz, pusty tekst zamiast obiektu czyści wszystko.",
                    "additionalProperties": {
                      "type": "string",
                      "maxLength": 500
                    },
                    "maxProperties": 50,
                    "propertyNames": {
                      "maxLength": 40
                    },
                    "examples": [
                      {
                        "order_id": "10482"
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Plan"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "module_not_in_plan: moduł Bill jest od planu Firma.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/coupons": {
      "post": {
        "tags": [
          "Kupony"
        ],
        "summary": "Utwórz kupon",
        "description": "Procent (percent_off) albo kwota (amount_off w groszach, z currency). duration: once (pierwszy okres), forever, repeating (duration_in_months).",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "operationId": "utw_rz_kupon",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "code": {
                    "type": "string",
                    "maxLength": 40,
                    "pattern": "^[A-Za-z0-9_-]+$"
                  },
                  "percent_off": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 100
                  },
                  "amount_off": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "currency": {
                    "type": "string",
                    "enum": [
                      "PLN",
                      "EUR",
                      "USD"
                    ],
                    "description": "Waluta. EUR i USD od planu Skala."
                  },
                  "duration": {
                    "type": "string",
                    "enum": [
                      "once",
                      "forever",
                      "repeating"
                    ]
                  },
                  "duration_in_months": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 36
                  },
                  "max_redemptions": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "metadata": {
                    "type": "object",
                    "description": "Własne pary klucz: wartość (do 50 kluczy, klucz do 40 znaków, wartość tekstowa do 500 znaków). Wracają w obiekcie i w zdarzeniach webhooka. Przy zmianie nowe klucze dochodzą do zapisanych, pusta wartość usuwa klucz, pusty tekst zamiast obiektu czyści wszystko.",
                    "additionalProperties": {
                      "type": "string",
                      "maxLength": 500
                    },
                    "maxProperties": 50,
                    "propertyNames": {
                      "maxLength": 40
                    },
                    "examples": [
                      {
                        "order_id": "10482"
                      }
                    ]
                  }
                },
                "required": [
                  "code",
                  "duration"
                ]
              },
              "example": {
                "code": "START20",
                "percent_off": 20,
                "duration": "once",
                "max_redemptions": 100
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Utworzono.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Coupon"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "module_not_in_plan: moduł Bill jest od planu Firma.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Kupony"
        ],
        "summary": "Lista kuponów",
        "description": "Kupony konta w trybie klucza.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 10
            },
            "description": "Liczba obiektów na stronie (1 do 100)."
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator obiektu; zwraca starsze od niego (następna strona)."
          },
          {
            "name": "ending_before",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator obiektu; zwraca nowsze od niego (poprzednia strona). Nie łączyć ze starting_after."
          },
          {
            "name": "created",
            "in": "query",
            "required": false,
            "style": "deepObject",
            "explode": true,
            "schema": {
              "type": "object",
              "properties": {
                "gt": {
                  "type": "integer"
                },
                "gte": {
                  "type": "integer"
                },
                "lt": {
                  "type": "integer"
                },
                "lte": {
                  "type": "integer"
                }
              }
            },
            "description": "Filtr po chwili utworzenia (Unix): created[gte]=1760000000."
          }
        ],
        "operationId": "lista_kupon_w",
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "url",
                    "has_more",
                    "data"
                  ],
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list",
                      "description": "Nazwa typu obiektu, zawsze \"list\"."
                    },
                    "url": {
                      "type": "string",
                      "examples": [
                        "/v1/coupons"
                      ]
                    },
                    "has_more": {
                      "type": "boolean",
                      "description": "true, gdy w tym kierunku są kolejne obiekty (następna strona: starting_after z id ostatniego obiektu)."
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Coupon"
                      },
                      "description": "Obiekty od najnowszego."
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "module_not_in_plan: moduł Bill jest od planu Firma.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/coupons/{id}": {
      "get": {
        "tags": [
          "Kupony"
        ],
        "summary": "Pobierz kupon",
        "description": "Kupon po identyfikatorze.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator coupon_."
          }
        ],
        "operationId": "pobierz_kupon",
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Coupon"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "module_not_in_plan: moduł Bill jest od planu Firma.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Kupony"
        ],
        "summary": "Usuń kupon",
        "description": "Kupon przestaje być przyjmowany; subskrypcje, które go mają, zachowują rabat.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator coupon_."
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "operationId": "usu_kupon",
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deleted"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "module_not_in_plan: moduł Bill jest od planu Firma.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/subscriptions": {
      "post": {
        "tags": [
          "Subskrypcje"
        ],
        "summary": "Utwórz subskrypcję",
        "description": "Wymagany tylko plan. Klient z pola customer (cus_) albo z customer_email. Odpowiedź ma status incomplete i checkout_url (strona zapisu). Po opłaceniu pierwszego okresu status active, zdarzenie subscription.created; każde odnowienie to payment.succeeded i subscription.renewed; nieudane odnowienie to subscription.payment_failed, status past_due i ponowienia Recover.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "operationId": "utw_rz_subskrypcj",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "plan": {
                    "type": "string",
                    "description": "Identyfikator planu plan_."
                  },
                  "customer": {
                    "type": "string",
                    "description": "Klient cus_."
                  },
                  "coupon": {
                    "type": "string",
                    "description": "Identyfikator coupon_ albo kod rabatowy."
                  },
                  "customer_email": {
                    "type": "string",
                    "format": "email"
                  },
                  "customer_name": {
                    "type": "string"
                  },
                  "tax_id": {
                    "type": "string"
                  },
                  "return_url": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 500,
                    "description": "Adres powrotu po zapłacie; klient wraca tam z parametrem ?payment=pay_... Stan płatności zawsze potwierdź przez GET /v1/payments/{id} albo webhook, nie z adresu."
                  },
                  "metadata": {
                    "type": "object",
                    "description": "Własne pary klucz: wartość (do 50 kluczy, klucz do 40 znaków, wartość tekstowa do 500 znaków). Wracają w obiekcie i w zdarzeniach webhooka. Przy zmianie nowe klucze dochodzą do zapisanych, pusta wartość usuwa klucz, pusty tekst zamiast obiektu czyści wszystko.",
                    "additionalProperties": {
                      "type": "string",
                      "maxLength": 500
                    },
                    "maxProperties": 50,
                    "propertyNames": {
                      "maxLength": 40
                    },
                    "examples": [
                      {
                        "order_id": "10482"
                      }
                    ]
                  }
                },
                "required": [
                  "plan"
                ]
              },
              "example": {
                "plan": "plan_Ab12Cd34Ef56Gh",
                "customer": "cus_Xy98Zw76Vu54Ts",
                "coupon": "START20",
                "return_url": "https://app.example.com/witaj"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Utworzono.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Subscription"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "coupon_invalid, parameter_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "module_not_in_plan: moduł Bill jest od planu Firma.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing (plan, customer).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Subskrypcje"
        ],
        "summary": "Lista subskrypcji",
        "description": "Subskrypcje konta w trybie klucza.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 10
            },
            "description": "Liczba obiektów na stronie (1 do 100)."
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator obiektu; zwraca starsze od niego (następna strona)."
          },
          {
            "name": "ending_before",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator obiektu; zwraca nowsze od niego (poprzednia strona). Nie łączyć ze starting_after."
          },
          {
            "name": "created",
            "in": "query",
            "required": false,
            "style": "deepObject",
            "explode": true,
            "schema": {
              "type": "object",
              "properties": {
                "gt": {
                  "type": "integer"
                },
                "gte": {
                  "type": "integer"
                },
                "lt": {
                  "type": "integer"
                },
                "lte": {
                  "type": "integer"
                }
              }
            },
            "description": "Filtr po chwili utworzenia (Unix): created[gte]=1760000000."
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "incomplete",
                "trialing",
                "active",
                "past_due",
                "canceled"
              ]
            },
            "description": "Filtr statusu."
          },
          {
            "name": "customer",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Subskrypcje klienta cus_."
          },
          {
            "name": "plan",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Subskrypcje planu plan_."
          },
          {
            "name": "expand[]",
            "in": "query",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "customer",
                  "plan",
                  "coupon"
                ]
              }
            },
            "description": "Pola do rozwinięcia w pełny obiekt (na liście z przedrostkiem data. albo bez). Rozwijalne: customer, plan, coupon."
          }
        ],
        "operationId": "lista_subskrypcji",
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "url",
                    "has_more",
                    "data"
                  ],
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list",
                      "description": "Nazwa typu obiektu, zawsze \"list\"."
                    },
                    "url": {
                      "type": "string",
                      "examples": [
                        "/v1/subscriptions"
                      ]
                    },
                    "has_more": {
                      "type": "boolean",
                      "description": "true, gdy w tym kierunku są kolejne obiekty (następna strona: starting_after z id ostatniego obiektu)."
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Subscription"
                      },
                      "description": "Obiekty od najnowszego."
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "module_not_in_plan: moduł Bill jest od planu Firma.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/subscriptions/{id}": {
      "get": {
        "tags": [
          "Subskrypcje"
        ],
        "summary": "Pobierz subskrypcję",
        "description": "Subskrypcja po identyfikatorze.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator sub_."
          },
          {
            "name": "expand[]",
            "in": "query",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "customer",
                  "plan",
                  "coupon"
                ]
              }
            },
            "description": "Pola do rozwinięcia w pełny obiekt (na liście z przedrostkiem data. albo bez). Rozwijalne: customer, plan, coupon."
          }
        ],
        "operationId": "pobierz_subskrypcj",
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Subscription"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "module_not_in_plan: moduł Bill jest od planu Firma.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Subskrypcje"
        ],
        "summary": "Zmień subskrypcję",
        "description": "plan: zmiana planu z proracją (pole proration w odpowiedzi: dopłata pobrana teraz i kredyt na następny okres); cancel_at_period_end: true kończy po opłaconym okresie, false cofa rezygnację; metadata. Pola można łączyć.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator sub_."
          }
        ],
        "operationId": "zmie_subskrypcj",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "plan": {
                    "type": "string"
                  },
                  "cancel_at_period_end": {
                    "type": "boolean"
                  },
                  "metadata": {
                    "type": "object",
                    "description": "Własne pary klucz: wartość (do 50 kluczy, klucz do 40 znaków, wartość tekstowa do 500 znaków). Wracają w obiekcie i w zdarzeniach webhooka. Przy zmianie nowe klucze dochodzą do zapisanych, pusta wartość usuwa klucz, pusty tekst zamiast obiektu czyści wszystko.",
                    "additionalProperties": {
                      "type": "string",
                      "maxLength": 500
                    },
                    "maxProperties": 50,
                    "propertyNames": {
                      "maxLength": 40
                    },
                    "examples": [
                      {
                        "order_id": "10482"
                      }
                    ]
                  }
                }
              },
              "example": {
                "plan": "plan_Zz99Yy88Xx77Ww"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Subscription"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "currency_mismatch, parameter_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "module_not_in_plan: moduł Bill jest od planu Firma.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "subscription_not_active.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "provider_error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/subscriptions/{id}/cancel": {
      "post": {
        "tags": [
          "Subskrypcje"
        ],
        "summary": "Anuluj subskrypcję",
        "description": "Domyślnie na koniec opłaconego okresu (cancel_at_period_end: true, status bez zmian); at_period_end: false kończy od razu (status canceled, zdarzenie subscription.canceled).",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator sub_."
          }
        ],
        "operationId": "anuluj_subskrypcj",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "at_period_end": {
                    "type": "boolean",
                    "default": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Subscription"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "module_not_in_plan: moduł Bill jest od planu Firma.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "subscription_not_active.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "provider_error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/invoices": {
      "post": {
        "tags": [
          "Faktury"
        ],
        "summary": "Wystaw fakturę z terminem",
        "description": "Faktura VAT do zapłaty (type payable) z własną stroną płatności (payment_url). Nabywca z customer (cus_) albo z pól customer_*. Z send (domyślnie true) i adresem e-mail nabywcy faktura idzie mailem, a przypomnienia według harmonogramu z panelu. Wymaga uzupełnionych danych firmy sprzedawcy w panelu.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "operationId": "wystaw_faktur_z_terminem",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "customer": {
                    "type": "string",
                    "description": "Klient cus_; jego nazwa, NIP, adres i e-mail trafiają na fakturę."
                  },
                  "customer_name": {
                    "type": "string",
                    "maxLength": 190
                  },
                  "customer_tax_id": {
                    "type": "string",
                    "maxLength": 20
                  },
                  "customer_address": {
                    "type": "string",
                    "maxLength": 255
                  },
                  "customer_email": {
                    "type": "string",
                    "format": "email"
                  },
                  "due_date": {
                    "type": "string",
                    "format": "date",
                    "description": "Termin płatności RRRR-MM-DD, nie wcześniej niż dziś."
                  },
                  "lines": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 50,
                    "items": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string",
                          "maxLength": 256
                        },
                        "quantity": {
                          "type": "number",
                          "minimum": 0.01,
                          "maximum": 100000
                        },
                        "unit_net": {
                          "type": "integer",
                          "minimum": 1,
                          "description": "Cena netto jednostki w groszach."
                        }
                      },
                      "required": [
                        "name",
                        "quantity",
                        "unit_net"
                      ]
                    }
                  },
                  "vat_rate": {
                    "type": "integer",
                    "description": "Stawka VAT w procentach (23, 8, 5, 0); domyślnie stawka z ustawień konta."
                  },
                  "send": {
                    "type": "boolean",
                    "default": true
                  },
                  "metadata": {
                    "type": "object",
                    "description": "Własne pary klucz: wartość (do 50 kluczy, klucz do 40 znaków, wartość tekstowa do 500 znaków). Wracają w obiekcie i w zdarzeniach webhooka. Przy zmianie nowe klucze dochodzą do zapisanych, pusta wartość usuwa klucz, pusty tekst zamiast obiektu czyści wszystko.",
                    "additionalProperties": {
                      "type": "string",
                      "maxLength": 500
                    },
                    "maxProperties": 50,
                    "propertyNames": {
                      "maxLength": 40
                    },
                    "examples": [
                      {
                        "order_id": "10482"
                      }
                    ]
                  }
                },
                "required": [
                  "due_date",
                  "lines"
                ]
              },
              "example": {
                "customer_name": "Firma Testowa sp. z o.o.",
                "customer_tax_id": "1234563218",
                "customer_email": "faktury@example.com",
                "due_date": "2026-10-17",
                "lines": [
                  {
                    "name": "Usługa wdrożenia",
                    "quantity": 1,
                    "unit_net": 100000
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Utworzono.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Invoice"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "seller_details_missing, amount_too_large, parameter_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "module_not_in_plan: faktury są od planu Firma.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing (customer).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Faktury"
        ],
        "summary": "Lista faktur",
        "description": "Faktury konta w trybie klucza (do zapłaty, po opłaceniu, korekty).",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 10
            },
            "description": "Liczba obiektów na stronie (1 do 100)."
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator obiektu; zwraca starsze od niego (następna strona)."
          },
          {
            "name": "ending_before",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator obiektu; zwraca nowsze od niego (poprzednia strona). Nie łączyć ze starting_after."
          },
          {
            "name": "created",
            "in": "query",
            "required": false,
            "style": "deepObject",
            "explode": true,
            "schema": {
              "type": "object",
              "properties": {
                "gt": {
                  "type": "integer"
                },
                "gte": {
                  "type": "integer"
                },
                "lt": {
                  "type": "integer"
                },
                "lte": {
                  "type": "integer"
                }
              }
            },
            "description": "Filtr po chwili utworzenia (Unix): created[gte]=1760000000."
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "partially_paid",
                "paid",
                "refunded"
              ]
            },
            "description": "Filtr statusu."
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "payable",
                "paid",
                "credit_note"
              ]
            },
            "description": "Filtr rodzaju."
          },
          {
            "name": "expand[]",
            "in": "query",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "payment",
                  "refund",
                  "corrects"
                ]
              }
            },
            "description": "Pola do rozwinięcia w pełny obiekt (na liście z przedrostkiem data. albo bez). Rozwijalne: payment, refund, corrects."
          }
        ],
        "operationId": "lista_faktur",
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "url",
                    "has_more",
                    "data"
                  ],
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list",
                      "description": "Nazwa typu obiektu, zawsze \"list\"."
                    },
                    "url": {
                      "type": "string",
                      "examples": [
                        "/v1/invoices"
                      ]
                    },
                    "has_more": {
                      "type": "boolean",
                      "description": "true, gdy w tym kierunku są kolejne obiekty (następna strona: starting_after z id ostatniego obiektu)."
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Invoice"
                      },
                      "description": "Obiekty od najnowszego."
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "module_not_in_plan: faktury są od planu Firma.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/invoices/{id}": {
      "get": {
        "tags": [
          "Faktury"
        ],
        "summary": "Pobierz fakturę",
        "description": "Faktura po identyfikatorze.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator inv_."
          },
          {
            "name": "expand[]",
            "in": "query",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "payment",
                  "refund",
                  "corrects"
                ]
              }
            },
            "description": "Pola do rozwinięcia w pełny obiekt (na liście z przedrostkiem data. albo bez). Rozwijalne: payment, refund, corrects."
          }
        ],
        "operationId": "pobierz_faktur",
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Invoice"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "module_not_in_plan: faktury są od planu Firma.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Faktury"
        ],
        "summary": "Zmień fakturę",
        "description": "Tylko metadata: treść wystawionej faktury jest stała, poprawki to faktura korygująca.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator inv_."
          }
        ],
        "operationId": "zmie_faktur",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "metadata": {
                    "type": "object",
                    "description": "Własne pary klucz: wartość (do 50 kluczy, klucz do 40 znaków, wartość tekstowa do 500 znaków). Wracają w obiekcie i w zdarzeniach webhooka. Przy zmianie nowe klucze dochodzą do zapisanych, pusta wartość usuwa klucz, pusty tekst zamiast obiektu czyści wszystko.",
                    "additionalProperties": {
                      "type": "string",
                      "maxLength": 500
                    },
                    "maxProperties": 50,
                    "propertyNames": {
                      "maxLength": 40
                    },
                    "examples": [
                      {
                        "order_id": "10482"
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Invoice"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "module_not_in_plan: faktury są od planu Firma.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/products": {
      "post": {
        "tags": [
          "Produkty"
        ],
        "summary": "Utwórz produkt",
        "description": "Produkt do sprzedaży bez sklepu: nazwa, opis, cena i typ. type recurring tworzy razem z produktem plan Bill (interval month albo year, opcjonalnie trial_days) i wymaga planu Firma. Zdjęcie dodaje się w panelu, w zakładce Strony sprzedaży. Po utworzeniu produktu utwórz dla niego stronę sprzedaży (POST /v1/checkout_pages).",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "operationId": "utw_rz_produkt",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 120
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 2000,
                    "description": "Zwykły tekst bez HTML."
                  },
                  "amount": {
                    "type": "integer",
                    "minimum": 100,
                    "maximum": 999999999,
                    "description": "Cena w groszach."
                  },
                  "currency": {
                    "type": "string",
                    "enum": [
                      "PLN",
                      "EUR",
                      "USD"
                    ],
                    "description": "Waluta. EUR i USD od planu Skala."
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "one_time",
                      "recurring"
                    ],
                    "default": "one_time"
                  },
                  "interval": {
                    "type": "string",
                    "enum": [
                      "month",
                      "year"
                    ],
                    "description": "Wymagane przy type recurring."
                  },
                  "trial_days": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 365,
                    "default": 0,
                    "description": "Dni okresu próbnego subskrypcji."
                  },
                  "metadata": {
                    "type": "object",
                    "description": "Własne pary klucz: wartość (do 50 kluczy, klucz do 40 znaków, wartość tekstowa do 500 znaków). Wracają w obiekcie i w zdarzeniach webhooka. Przy zmianie nowe klucze dochodzą do zapisanych, pusta wartość usuwa klucz, pusty tekst zamiast obiektu czyści wszystko.",
                    "additionalProperties": {
                      "type": "string",
                      "maxLength": 500
                    },
                    "maxProperties": 50,
                    "propertyNames": {
                      "maxLength": 40
                    },
                    "examples": [
                      {
                        "order_id": "10482"
                      }
                    ]
                  }
                },
                "required": [
                  "name",
                  "amount",
                  "currency"
                ]
              },
              "example": {
                "name": "Kurs fotografii online",
                "description": "Dostęp do nagrań na rok.",
                "amount": 12900,
                "currency": "PLN",
                "metadata": {
                  "sku": "KURS-1"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Utworzono.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Product"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "module_not_in_plan (subskrypcja albo pole coupon od planu Firma), currency_not_in_plan.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Produkty"
        ],
        "summary": "Lista produktów",
        "description": "Produkty konta w trybie klucza.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 10
            },
            "description": "Liczba obiektów na stronie (1 do 100)."
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator obiektu; zwraca starsze od niego (następna strona)."
          },
          {
            "name": "ending_before",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator obiektu; zwraca nowsze od niego (poprzednia strona). Nie łączyć ze starting_after."
          },
          {
            "name": "created",
            "in": "query",
            "required": false,
            "style": "deepObject",
            "explode": true,
            "schema": {
              "type": "object",
              "properties": {
                "gt": {
                  "type": "integer"
                },
                "gte": {
                  "type": "integer"
                },
                "lt": {
                  "type": "integer"
                },
                "lte": {
                  "type": "integer"
                }
              }
            },
            "description": "Filtr po chwili utworzenia (Unix): created[gte]=1760000000."
          },
          {
            "name": "active",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Tylko włączone albo wyłączone."
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "one_time",
                "recurring"
              ]
            },
            "description": "Filtr typu."
          },
          {
            "name": "expand[]",
            "in": "query",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "plan"
                ]
              }
            },
            "description": "Pola do rozwinięcia w pełny obiekt (na liście z przedrostkiem data. albo bez). Rozwijalne: plan."
          }
        ],
        "operationId": "lista_produkt_w",
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "url",
                    "has_more",
                    "data"
                  ],
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list",
                      "description": "Nazwa typu obiektu, zawsze \"list\"."
                    },
                    "url": {
                      "type": "string",
                      "examples": [
                        "/v1/products"
                      ]
                    },
                    "has_more": {
                      "type": "boolean",
                      "description": "true, gdy w tym kierunku są kolejne obiekty (następna strona: starting_after z id ostatniego obiektu)."
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Product"
                      },
                      "description": "Obiekty od najnowszego."
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/products/{id}": {
      "get": {
        "tags": [
          "Produkty"
        ],
        "summary": "Pobierz produkt",
        "description": "Produkt po identyfikatorze.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator prod_."
          },
          {
            "name": "expand[]",
            "in": "query",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "plan"
                ]
              }
            },
            "description": "Pola do rozwinięcia w pełny obiekt (na liście z przedrostkiem data. albo bez). Rozwijalne: plan."
          }
        ],
        "operationId": "pobierz_produkt",
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Product"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Produkty"
        ],
        "summary": "Zmień produkt",
        "description": "name, description, active (wyłączony produkt nie przyjmuje zakupów), metadata. Próba zmiany amount, currency, type albo interval to 400 parameter_invalid: utwórz nowy produkt.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator prod_."
          }
        ],
        "operationId": "zmie_produkt",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 120
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 2000
                  },
                  "active": {
                    "type": "boolean"
                  },
                  "metadata": {
                    "type": "object",
                    "description": "Własne pary klucz: wartość (do 50 kluczy, klucz do 40 znaków, wartość tekstowa do 500 znaków). Wracają w obiekcie i w zdarzeniach webhooka. Przy zmianie nowe klucze dochodzą do zapisanych, pusta wartość usuwa klucz, pusty tekst zamiast obiektu czyści wszystko.",
                    "additionalProperties": {
                      "type": "string",
                      "maxLength": 500
                    },
                    "maxProperties": 50,
                    "propertyNames": {
                      "maxLength": 40
                    },
                    "examples": [
                      {
                        "order_id": "10482"
                      }
                    ]
                  }
                }
              },
              "example": {
                "active": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Product"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/checkout_pages": {
      "post": {
        "tags": [
          "Strony sprzedaży"
        ],
        "summary": "Utwórz stronę sprzedaży",
        "description": "Publiczna strona zakupu produktu w marce sprzedawcy (kolor i logo z panelu), bez sklepu i bez kodu. Odpowiedź ma url do wysłania klientom, qr_code_url i button_html do wklejenia na dowolną stronę. Zakup tworzy płatność (pay_) z metadata.checkout_page albo subskrypcję (sub_) i daje te same zdarzenia payment.* i subscription.* co API; kupujący zostaje klientem (cus_). Po zapłacie klient trafia na success_url (z ?payment= albo ?subscription=) albo na stronę podziękowania z message.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "operationId": "utw_rz_stron_sprzeda_y",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "product": {
                    "type": "string",
                    "description": "Produkt prod_ (włączony)."
                  },
                  "fields": {
                    "type": "object",
                    "description": "Pola dla kupującego poza e-mailem i nazwą (zawsze obecne): address (adres), tax_id (NIP do faktury), phone (telefon), coupon (kod rabatowy z kuponów Bill, od planu Firma).",
                    "properties": {
                      "address": {
                        "type": "boolean"
                      },
                      "tax_id": {
                        "type": "boolean"
                      },
                      "phone": {
                        "type": "boolean"
                      },
                      "coupon": {
                        "type": "boolean"
                      }
                    }
                  },
                  "quantity_max": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 99,
                    "default": 1,
                    "description": "Największa ilość w zamówieniu (tylko one_time)."
                  },
                  "success_url": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 500,
                    "description": "Adres https po zapłacie."
                  },
                  "message": {
                    "type": "string",
                    "maxLength": 1000,
                    "description": "Komunikat na stronie podziękowania."
                  },
                  "metadata": {
                    "type": "object",
                    "description": "Własne pary klucz: wartość (do 50 kluczy, klucz do 40 znaków, wartość tekstowa do 500 znaków). Wracają w obiekcie i w zdarzeniach webhooka. Przy zmianie nowe klucze dochodzą do zapisanych, pusta wartość usuwa klucz, pusty tekst zamiast obiektu czyści wszystko.",
                    "additionalProperties": {
                      "type": "string",
                      "maxLength": 500
                    },
                    "maxProperties": 50,
                    "propertyNames": {
                      "maxLength": 40
                    },
                    "examples": [
                      {
                        "order_id": "10482"
                      }
                    ]
                  }
                },
                "required": [
                  "product"
                ]
              },
              "example": {
                "product": "prod_Ab12Cd34Ef56Gh",
                "fields": {
                  "tax_id": true
                },
                "message": "Dostęp wyślemy w ciągu godziny."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Utworzono.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckoutPage"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "provider_not_connected (produkcja).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "sandbox_limit_reached, module_not_in_plan (fields.coupon od planu Firma).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing (product).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "product_inactive.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Strony sprzedaży"
        ],
        "summary": "Lista stron sprzedaży",
        "description": "Strony konta w trybie klucza, ze statystykami.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 10
            },
            "description": "Liczba obiektów na stronie (1 do 100)."
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator obiektu; zwraca starsze od niego (następna strona)."
          },
          {
            "name": "ending_before",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator obiektu; zwraca nowsze od niego (poprzednia strona). Nie łączyć ze starting_after."
          },
          {
            "name": "created",
            "in": "query",
            "required": false,
            "style": "deepObject",
            "explode": true,
            "schema": {
              "type": "object",
              "properties": {
                "gt": {
                  "type": "integer"
                },
                "gte": {
                  "type": "integer"
                },
                "lt": {
                  "type": "integer"
                },
                "lte": {
                  "type": "integer"
                }
              }
            },
            "description": "Filtr po chwili utworzenia (Unix): created[gte]=1760000000."
          },
          {
            "name": "active",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Tylko włączone albo wyłączone."
          },
          {
            "name": "product",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Strony jednego produktu prod_."
          },
          {
            "name": "expand[]",
            "in": "query",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "product"
                ]
              }
            },
            "description": "Pola do rozwinięcia w pełny obiekt (na liście z przedrostkiem data. albo bez). Rozwijalne: product."
          }
        ],
        "operationId": "lista_stron_sprzeda_y",
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "url",
                    "has_more",
                    "data"
                  ],
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list",
                      "description": "Nazwa typu obiektu, zawsze \"list\"."
                    },
                    "url": {
                      "type": "string",
                      "examples": [
                        "/v1/checkout_pages"
                      ]
                    },
                    "has_more": {
                      "type": "boolean",
                      "description": "true, gdy w tym kierunku są kolejne obiekty (następna strona: starting_after z id ostatniego obiektu)."
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CheckoutPage"
                      },
                      "description": "Obiekty od najnowszego."
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/checkout_pages/{id}": {
      "get": {
        "tags": [
          "Strony sprzedaży"
        ],
        "summary": "Pobierz stronę sprzedaży",
        "description": "Strona po identyfikatorze, ze statystykami (views, payments, revenue).",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator cpage_."
          },
          {
            "name": "expand[]",
            "in": "query",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "product"
                ]
              }
            },
            "description": "Pola do rozwinięcia w pełny obiekt (na liście z przedrostkiem data. albo bez). Rozwijalne: product."
          }
        ],
        "operationId": "pobierz_stron_sprzeda_y",
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckoutPage"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Strony sprzedaży"
        ],
        "summary": "Zmień stronę sprzedaży",
        "description": "active (wyłączenie i włączenie), fields, quantity_max, success_url, message, metadata. Produkt strony jest stały.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator cpage_."
          }
        ],
        "operationId": "zmie_stron_sprzeda_y",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "active": {
                    "type": "boolean"
                  },
                  "fields": {
                    "type": "object",
                    "description": "Pola dla kupującego poza e-mailem i nazwą (zawsze obecne): address (adres), tax_id (NIP do faktury), phone (telefon), coupon (kod rabatowy z kuponów Bill, od planu Firma).",
                    "properties": {
                      "address": {
                        "type": "boolean"
                      },
                      "tax_id": {
                        "type": "boolean"
                      },
                      "phone": {
                        "type": "boolean"
                      },
                      "coupon": {
                        "type": "boolean"
                      }
                    }
                  },
                  "quantity_max": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 99
                  },
                  "success_url": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 500
                  },
                  "message": {
                    "type": "string",
                    "maxLength": 1000
                  },
                  "metadata": {
                    "type": "object",
                    "description": "Własne pary klucz: wartość (do 50 kluczy, klucz do 40 znaków, wartość tekstowa do 500 znaków). Wracają w obiekcie i w zdarzeniach webhooka. Przy zmianie nowe klucze dochodzą do zapisanych, pusta wartość usuwa klucz, pusty tekst zamiast obiektu czyści wszystko.",
                    "additionalProperties": {
                      "type": "string",
                      "maxLength": 500
                    },
                    "maxProperties": 50,
                    "propertyNames": {
                      "maxLength": 40
                    },
                    "examples": [
                      {
                        "order_id": "10482"
                      }
                    ]
                  }
                }
              },
              "example": {
                "active": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckoutPage"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/portal_sessions": {
      "post": {
        "tags": [
          "Portal klienta"
        ],
        "summary": "Utwórz sesję portalu klienta",
        "description": "Jednorazowy adres wejścia do portalu klienta dla klienta cus_ albo adresu e-mail: wstaw go w przycisku „Moje płatności” w swojej aplikacji albo w mailu. Klient widzi tylko swoje płatności, faktury PDF i subskrypcje i robi to, na co pozwalają ustawienia portalu w panelu (rezygnacja z końcem okresu, zmiana planu, nowa karta, dane do faktury). Każda zmiana wysyła subscription.updated albo customer.updated. Adres działa godzinę i raz. Portal musi być włączony w panelu (inaczej 400 portal_disabled).",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "operationId": "utw_rz_sesj_portalu_klienta",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "customer": {
                    "type": "string",
                    "description": "Klient cus_ (wymagany, gdy nie ma customer_email)."
                  },
                  "customer_email": {
                    "type": "string",
                    "format": "email",
                    "description": "Adres klienta (wymagany, gdy nie ma customer)."
                  },
                  "return_url": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 500,
                    "description": "Adres https Twojej aplikacji, do którego klient wraca z portalu."
                  }
                }
              },
              "example": {
                "customer": "cus_Xy98Zw76Vu54Ts",
                "return_url": "https://app.example.com/konto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Utworzono.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PortalSession"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "portal_disabled, parameter_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing (customer).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/portal_sessions/{id}": {
      "get": {
        "tags": [
          "Portal klienta"
        ],
        "summary": "Pobierz sesję portalu",
        "description": "Sesja po identyfikatorze, bez adresu wejścia (url wraca tylko przy utworzeniu).",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator bps_."
          },
          {
            "name": "expand[]",
            "in": "query",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "customer"
                ]
              }
            },
            "description": "Pola do rozwinięcia w pełny obiekt (na liście z przedrostkiem data. albo bez). Rozwijalne: customer."
          }
        ],
        "operationId": "pobierz_sesj_portalu",
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PortalSession"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/events": {
      "get": {
        "tags": [
          "Zdarzenia"
        ],
        "summary": "Lista zdarzeń",
        "description": "Historia zdarzeń konta w trybie klucza; to samo, co poszło (albo idzie) na webhook. Do odzyskania zdarzeń po awarii własnego serwera: pobierz zdarzenia z created[gte] i delivered=false.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 10
            },
            "description": "Liczba obiektów na stronie (1 do 100)."
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator obiektu; zwraca starsze od niego (następna strona)."
          },
          {
            "name": "ending_before",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator obiektu; zwraca nowsze od niego (poprzednia strona). Nie łączyć ze starting_after."
          },
          {
            "name": "created",
            "in": "query",
            "required": false,
            "style": "deepObject",
            "explode": true,
            "schema": {
              "type": "object",
              "properties": {
                "gt": {
                  "type": "integer"
                },
                "gte": {
                  "type": "integer"
                },
                "lt": {
                  "type": "integer"
                },
                "lte": {
                  "type": "integer"
                }
              }
            },
            "description": "Filtr po chwili utworzenia (Unix): created[gte]=1760000000."
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Pełny typ (payment.succeeded) albo rodzina z gwiazdką (payment.*)."
          },
          {
            "name": "delivered",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "true: doręczone na webhook; false: niedoręczone (czekają na ponowienie albo bez adresu)."
          }
        ],
        "operationId": "lista_zdarze",
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "url",
                    "has_more",
                    "data"
                  ],
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list",
                      "description": "Nazwa typu obiektu, zawsze \"list\"."
                    },
                    "url": {
                      "type": "string",
                      "examples": [
                        "/v1/events"
                      ]
                    },
                    "has_more": {
                      "type": "boolean",
                      "description": "true, gdy w tym kierunku są kolejne obiekty (następna strona: starting_after z id ostatniego obiektu)."
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Event"
                      },
                      "description": "Obiekty od najnowszego."
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/events/{id}": {
      "get": {
        "tags": [
          "Zdarzenia"
        ],
        "summary": "Pobierz zdarzenie",
        "description": "Zdarzenie po identyfikatorze, ze stanem doręczenia (delivery).",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator evt_."
          }
        ],
        "operationId": "pobierz_zdarzenie",
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Event"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/events/{id}/resend": {
      "post": {
        "tags": [
          "Zdarzenia"
        ],
        "summary": "Wyślij zdarzenie ponownie",
        "description": "Jedna próba doręczenia teraz, bez zmiany harmonogramu ponowień. Wynik w polu delivery.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator evt_."
          }
        ],
        "operationId": "wy_lij_zdarzenie_ponownie",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {}
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Event"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "webhook_not_configured.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhook_endpoints": {
      "get": {
        "tags": [
          "Webhooki"
        ],
        "summary": "Lista adresów webhooka",
        "description": "Adres webhooka trybu klucza (jeden na tryb) albo pusta lista.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          }
        ],
        "operationId": "lista_adres_w_webhooka",
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "url",
                    "has_more",
                    "data"
                  ],
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list",
                      "description": "Nazwa typu obiektu, zawsze \"list\"."
                    },
                    "url": {
                      "type": "string",
                      "examples": [
                        "/v1/webhook_endpoints"
                      ]
                    },
                    "has_more": {
                      "type": "boolean",
                      "description": "true, gdy w tym kierunku są kolejne obiekty (następna strona: starting_after z id ostatniego obiektu)."
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookEndpoint"
                      },
                      "description": "Obiekty od najnowszego."
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Webhooki"
        ],
        "summary": "Zapisz adres webhooka",
        "description": "Adres https na publicznym serwerze i lista zdarzeń (albo [\"*\"]). Odpowiedź zawiera sekret whsec_ do sprawdzania podpisu; zapisz go, nie wraca w GET. Ponowny POST zastępuje adres i wydaje nowy sekret.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "operationId": "zapisz_adres_webhooka",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 500
                  },
                  "enabled_events": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "payment.created",
                        "payment.succeeded",
                        "payment.failed",
                        "payment.canceled",
                        "payment.routed",
                        "payment_link.expired",
                        "account.verified",
                        "payout.paid",
                        "refund.created",
                        "refund.succeeded",
                        "refund.failed",
                        "customer.created",
                        "customer.updated",
                        "customer.deleted",
                        "subscription.created",
                        "subscription.updated",
                        "subscription.renewed",
                        "subscription.payment_failed",
                        "subscription.recovered",
                        "subscription.canceled",
                        "invoice.issued",
                        "invoice.paid",
                        "invoice.ksef_accepted",
                        "invoice.ksef_rejected",
                        "*"
                      ]
                    },
                    "default": [
                      "*"
                    ]
                  }
                },
                "required": [
                  "url"
                ]
              },
              "example": {
                "url": "https://sklep.example.com/oplac/webhook",
                "enabled_events": [
                  "payment.succeeded",
                  "payment.failed",
                  "refund.succeeded"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Utworzono.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpoint"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhook_endpoints/{id}": {
      "get": {
        "tags": [
          "Webhooki"
        ],
        "summary": "Pobierz adres webhooka",
        "description": "Adres po identyfikatorze (bez sekretu).",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator we_."
          }
        ],
        "operationId": "pobierz_adres_webhooka",
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpoint"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Webhooki"
        ],
        "summary": "Zmień adres webhooka",
        "description": "url, enabled_events, rotate_secret (true: nowy sekret w odpowiedzi, stary przestaje działać od razu).",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator we_."
          }
        ],
        "operationId": "zmie_adres_webhooka",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "enabled_events": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "rotate_secret": {
                    "type": "boolean",
                    "default": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpoint"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Webhooki"
        ],
        "summary": "Usuń adres webhooka",
        "description": "Zdarzenia zostają w historii (GET /v1/events), nic nie jest wysyłane.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator we_."
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "operationId": "usu_adres_webhooka",
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deleted"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/test_helpers/payments/{id}/confirm": {
      "post": {
        "tags": [
          "Piaskownica"
        ],
        "summary": "Ustaw wynik płatności testowej",
        "description": "Zamiast klikać na stronie płatności piaskownicy. outcome: succeeded (domyślnie) albo failed (card_declined); blik_code: 777123 sukces, 777000 odmowa (blik_rejected), 777408 zły kod (blik_code_invalid). Uruchamia te same zdarzenia, webhooki i faktury co prawdziwa zapłata.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator pay_ płatności testowej."
          }
        ],
        "operationId": "ustaw_wynik_p_atno_ci_testowej",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "outcome": {
                    "type": "string",
                    "enum": [
                      "succeeded",
                      "failed"
                    ],
                    "default": "succeeded"
                  },
                  "method": {
                    "type": "string",
                    "enum": [
                      "blik",
                      "card"
                    ],
                    "default": "card"
                  },
                  "blik_code": {
                    "type": "string",
                    "enum": [
                      "777123",
                      "777000",
                      "777408"
                    ]
                  }
                }
              },
              "example": {
                "blik_code": "777123"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Payment"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "test_helpers_live_mode: pomocnicy testowi działają tylko z kluczem sk_test_.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "payment_not_confirmable: płatność już ma wynik.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/test_helpers/subscriptions/{id}/confirm": {
      "post": {
        "tags": [
          "Piaskownica"
        ],
        "summary": "Opłać pierwszy okres subskrypcji testowej",
        "description": "Subskrypcja incomplete przechodzi w active (albo trialing przy okresie próbnym) i wysyła subscription.created; outcome failed zostawia incomplete z nieudaną płatnością.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator sub_ subskrypcji testowej."
          }
        ],
        "operationId": "op_a_pierwszy_okres_subskrypcji_testowej",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "outcome": {
                    "type": "string",
                    "enum": [
                      "succeeded",
                      "failed"
                    ],
                    "default": "succeeded"
                  },
                  "method": {
                    "type": "string",
                    "enum": [
                      "blik",
                      "card"
                    ],
                    "default": "card"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Subscription"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "test_helpers_live_mode: pomocnicy testowi działają tylko z kluczem sk_test_.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "subscription_not_active: subskrypcja nie jest incomplete.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/test_helpers/subscriptions/{id}/renew": {
      "post": {
        "tags": [
          "Piaskownica"
        ],
        "summary": "Symuluj odnowienie subskrypcji testowej",
        "description": "Odnowienie przed terminem: succeeded (payment.succeeded, subscription.renewed), failed (odmowa przejściowa insufficient_funds, status past_due, subscription.payment_failed, Recover ponawia) albo failed_permanently (stolen_card, odmowa trwała).",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identyfikator sub_ subskrypcji testowej."
          }
        ],
        "operationId": "symuluj_odnowienie_subskrypcji_testowej",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "outcome": {
                    "type": "string",
                    "enum": [
                      "succeeded",
                      "failed",
                      "failed_permanently"
                    ],
                    "default": "succeeded"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Subscription"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Błąd żądania: parameter_missing, parameter_invalid, metadata_invalid, expand_invalid, api_version_invalid, idempotency_key_reused, idempotency_key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "test_helpers_live_mode: pomocnicy testowi działają tylko z kluczem sk_test_.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "resource_missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "subscription_not_active.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/test_helpers/webhooks": {
      "post": {
        "tags": [
          "Piaskownica"
        ],
        "summary": "Wyślij zdarzenie próbne",
        "description": "Zdarzenie wybranego typu z przykładowym obiektem na adres webhooka trybu testowego, z prawdziwym podpisem. Sprawdza adres, podpis i obsługę typu po stronie integracji.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OplacVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "operationId": "wy_lij_zdarzenie_pr_bne",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "payment.created",
                      "payment.succeeded",
                      "payment.failed",
                      "payment.canceled",
                      "payment.routed",
                      "payment_link.expired",
                      "account.verified",
                      "payout.paid",
                      "refund.created",
                      "refund.succeeded",
                      "refund.failed",
                      "customer.created",
                      "customer.updated",
                      "customer.deleted",
                      "subscription.created",
                      "subscription.updated",
                      "subscription.renewed",
                      "subscription.payment_failed",
                      "subscription.recovered",
                      "subscription.canceled",
                      "invoice.issued",
                      "invoice.paid",
                      "invoice.ksef_accepted",
                      "invoice.ksef_rejected"
                    ],
                    "default": "payment.succeeded"
                  }
                }
              },
              "example": {
                "type": "payment.succeeded"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Utworzono.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Event"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Oplac-Version": {
                "$ref": "#/components/headers/Oplac-Version"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "webhook_not_configured.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "api_key_invalid: brak nagłówka Authorization, klucz nieważny albo konto bez planu dla tego trybu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "test_helpers_live_mode: pomocnicy testowi działają tylko z kluczem sk_test_.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited. Nagłówek Retry-After mówi, ile sekund odczekać.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "api_error. Ponów żądanie; podaj request_id w zgłoszeniu.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Klucz API sk_test_... albo sk_live_... z panelu (zakładka Deweloperzy)."
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 255
        },
        "description": "Klucz idempotencji (np. UUID v4). Powtórka z tym samym kluczem i treścią w ciągu 24 godzin zwraca pierwszy wynik."
      },
      "OplacVersion": {
        "name": "Oplac-Version",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string",
          "enum": [
            "2026-10-03"
          ]
        },
        "description": "Wersja API. Domyślnie najnowsza."
      }
    },
    "headers": {
      "X-Request-Id": {
        "schema": {
          "type": "string"
        },
        "description": "Identyfikator żądania req_, ten sam co w treści błędu."
      },
      "Oplac-Version": {
        "schema": {
          "type": "string"
        },
        "description": "Użyta wersja API."
      },
      "X-RateLimit-Limit": {
        "schema": {
          "type": "integer"
        },
        "description": "Limit żądań na sekundę dla konta i trybu."
      },
      "X-RateLimit-Remaining": {
        "schema": {
          "type": "integer"
        },
        "description": "Ile żądań zostało w bieżącej sekundzie."
      },
      "X-RateLimit-Reset": {
        "schema": {
          "type": "integer"
        },
        "description": "Za ile sekund okno się odnawia."
      },
      "Retry-After": {
        "schema": {
          "type": "integer"
        },
        "description": "Przy 429: za ile sekund ponowić."
      },
      "Idempotent-Replayed": {
        "schema": {
          "type": "string",
          "const": "true"
        },
        "description": "Odpowiedź odtworzona z zapisu idempotencji."
      },
      "Original-Request": {
        "schema": {
          "type": "string"
        },
        "description": "Przy odtworzeniu: X-Request-Id pierwszego żądania."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "description": "Jeden obiekt błędu dla każdej odpowiedzi 4xx i 5xx, także 404 nieznanej trasy, 405, 429 i 500. Nigdy HTML.",
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "type",
              "code",
              "message",
              "param",
              "doc_url",
              "request_id"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "invalid_request_error",
                  "authentication_error",
                  "idempotency_error",
                  "rate_limit_error",
                  "api_error"
                ],
                "description": "Klasa błędu. invalid_request_error: żądanie do poprawy; authentication_error: klucz; idempotency_error: nagłówek Idempotency-Key; rate_limit_error: limit zapytań; api_error: błąd po stronie Oplac albo dostawcy płatności."
              },
              "code": {
                "type": "string",
                "description": "Stały kod do obsługi w kodzie integracji. Pełny katalog kodów w sekcji Błędy dokumentacji i w polu x-oplac-error-codes tej specyfikacji.",
                "examples": [
                  "parameter_missing"
                ]
              },
              "message": {
                "type": "string",
                "description": "Komunikat po polsku, mówi co poprawić."
              },
              "param": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Nazwa pola żądania, którego dotyczy błąd (kropka dla pól zagnieżdżonych, np. customer.email), albo null."
              },
              "doc_url": {
                "type": "string",
                "format": "uri",
                "description": "Opis kodu w dokumentacji: https://oplac.pl/docs/bledy#<code>."
              },
              "request_id": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Identyfikator żądania (req_), ten sam co w nagłówku X-Request-Id. Podaj go w zgłoszeniu."
              }
            }
          }
        },
        "examples": [
          {
            "error": {
              "type": "invalid_request_error",
              "code": "parameter_missing",
              "message": "Podaj kwotę w groszach w polu amount, np. 12900 dla 129,00 zł.",
              "param": "amount",
              "doc_url": "https://oplac.pl/docs/bledy#parameter_missing",
              "request_id": "req_7mK2pQ9rT4vX1bN8cD3fG6hJ"
            }
          }
        ]
      },
      "Account": {
        "type": "object",
        "description": "Konto sprzedawcy (acct_) z punktu widzenia klucza, którym pyta integracja. Oplac jest oprogramowaniem działającym na koncie dostawcy płatności sprzedawcy i nie przyjmuje ani nie przechowuje środków.",
        "required": [
          "id",
          "object",
          "plan",
          "modules",
          "currencies",
          "charges_enabled",
          "provider_connected",
          "webhook_configured",
          "livemode"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Identyfikator konta.",
            "pattern": "^acct_[A-Za-z0-9_]+$",
            "examples": [
              "acct_Ab12Cd34Ef56Gh"
            ]
          },
          "object": {
            "type": "string",
            "const": "account",
            "description": "Nazwa typu obiektu, zawsze \"account\"."
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": "string"
          },
          "plan": {
            "type": [
              "string",
              "null"
            ],
            "description": "start, firma, skala, platforma, demo (piaskownica po zapisie) albo null bez planu."
          },
          "modules": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Moduły planu: accept, api, bill, faktury, invoices, waluty, routing, connect i inne."
          },
          "currencies": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "charges_enabled": {
            "type": "boolean",
            "description": "Czy w tym trybie płatności przejdą: produkcja wymaga planu i podpiętego dostawcy, piaskownica działa zawsze."
          },
          "provider_connected": {
            "type": "boolean"
          },
          "webhook_configured": {
            "type": "boolean"
          },
          "livemode": {
            "type": "boolean",
            "description": "true dla obiektów produkcyjnych (klucz sk_live_), false dla piaskownicy (sk_test_)."
          },
          "created": {
            "type": "integer",
            "description": "Chwila utworzenia jako znacznik czasu Unix (sekundy, UTC).",
            "examples": [
              1760000000
            ]
          }
        },
        "examples": [
          {
            "id": "acct_Ab12Cd34Ef56Gh",
            "object": "account",
            "name": "Sklep Testowy",
            "email": "app@sklep.example.com",
            "plan": "firma",
            "modules": [
              "accept",
              "api",
              "zespol",
              "plan",
              "bill",
              "faktury",
              "ksef",
              "recover",
              "invoices"
            ],
            "currencies": [
              "PLN"
            ],
            "charges_enabled": true,
            "provider_connected": false,
            "webhook_configured": true,
            "livemode": false,
            "created": 1760000000
          }
        ]
      },
      "Payment": {
        "type": "object",
        "description": "Płatność (pay_). Tworzona z API, z linku do płatności, z faktury albo z okresu subskrypcji. Hostowana strona płatności pod checkout_url: w piaskownicy u nas (kody BLIK i przyciski wyniku), w produkcji u dostawcy płatności sprzedawcy.",
        "required": [
          "id",
          "object",
          "amount",
          "currency",
          "status",
          "checkout_url",
          "metadata",
          "livemode",
          "created"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Identyfikator płatności.",
            "pattern": "^pay_[A-Za-z0-9_]+$",
            "examples": [
              "pay_Ab12Cd34Ef56Gh"
            ]
          },
          "object": {
            "type": "string",
            "const": "payment",
            "description": "Nazwa typu obiektu, zawsze \"payment\"."
          },
          "amount": {
            "type": "integer",
            "description": "Kwota w groszach (najmniejszej jednostce waluty).",
            "examples": [
              12900
            ]
          },
          "amount_refunded": {
            "type": "integer",
            "description": "Suma udanych zwrotów w groszach."
          },
          "currency": {
            "type": "string",
            "enum": [
              "PLN",
              "EUR",
              "USD"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "requires_confirmation",
              "processing",
              "succeeded",
              "failed",
              "canceled"
            ],
            "description": "pending: czeka na klienta; requires_confirmation: klient jest w trakcie potwierdzania (np. 3D Secure); processing: dostawca przetwarza; succeeded: opłacona; failed: odrzucona; canceled: anulowana albo wygasła."
          },
          "method": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "blik",
              "card",
              "transfer",
              null
            ],
            "description": "Metoda, którą klient zapłacił; null przed zapłatą."
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 255
          },
          "checkout_url": {
            "type": "string",
            "format": "uri",
            "description": "Hostowana strona płatności. Skieruj na nią klienta (przekierowanie 302 albo link)."
          },
          "return_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Adres powrotu po zapłacie; klient wraca tam z parametrem ?payment=pay_..."
          },
          "customer": {
            "type": [
              "string",
              "null"
            ],
            "description": "Identyfikator klienta (cus_) albo pełny obiekt przy expand[]=customer."
          },
          "customer_email": {
            "type": [
              "string",
              "null"
            ]
          },
          "customer_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "payment_link": {
            "type": [
              "string",
              "null"
            ],
            "description": "Link (plink_), z którego powstała płatność."
          },
          "subscription": {
            "type": [
              "string",
              "null"
            ],
            "description": "Subskrypcja (sub_), której okres opłaca ta płatność."
          },
          "invoice": {
            "type": [
              "string",
              "null"
            ],
            "description": "Faktura (inv_), którą opłaca ta płatność."
          },
          "tax_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "NIP podany przez klienta na stronie płatności."
          },
          "provider_account": {
            "type": [
              "string",
              "null"
            ],
            "description": "Konto dostawcy, które obsłużyło płatność (Smart routing): \"primary\" albo identyfikator konta dodatkowego. Tylko produkcja."
          },
          "connected_account": {
            "type": [
              "string",
              "null"
            ],
            "description": "Sprzedawca platformy (Connect), do którego trafia płatność po potrąceniu prowizji."
          },
          "application_fee": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Prowizja platformy w groszach (Connect)."
          },
          "failure_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Kod odmowy: card_declined, blik_rejected, blik_code_invalid, insufficient_funds, stolen_card."
          },
          "decline_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Surowy kod odmowy od dostawcy płatności (produkcja)."
          },
          "metadata": {
            "type": "object",
            "description": "Własne pary klucz: wartość (do 50 kluczy, klucz do 40 znaków, wartość tekstowa do 500 znaków). Wracają w obiekcie i w zdarzeniach webhooka. Przy zmianie nowe klucze dochodzą do zapisanych, pusta wartość usuwa klucz, pusty tekst zamiast obiektu czyści wszystko.",
            "additionalProperties": {
              "type": "string",
              "maxLength": 500
            },
            "maxProperties": 50,
            "propertyNames": {
              "maxLength": 40
            },
            "examples": [
              {
                "order_id": "10482"
              }
            ]
          },
          "livemode": {
            "type": "boolean",
            "description": "true dla obiektów produkcyjnych (klucz sk_live_), false dla piaskownicy (sk_test_)."
          },
          "expires_at": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Do kiedy strona płatności przyjmuje zapłatę (Unix). Domyślnie 24 godziny."
          },
          "paid_at": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Chwila zapłaty (Unix) albo null."
          },
          "created": {
            "type": "integer",
            "description": "Chwila utworzenia jako znacznik czasu Unix (sekundy, UTC).",
            "examples": [
              1760000000
            ]
          }
        },
        "examples": [
          {
            "id": "pay_Ab12Cd34Ef56Gh",
            "object": "payment",
            "amount": 12900,
            "amount_refunded": 0,
            "currency": "PLN",
            "status": "pending",
            "method": null,
            "description": "Zamówienie 10482",
            "checkout_url": "https://oplac.pl/p/Ab12Cd34Ef56Gh",
            "return_url": "https://sklep.example.com/powrot",
            "customer": "cus_Xy98Zw76Vu54Ts",
            "customer_email": "anna@example.com",
            "customer_name": "Anna Nowak",
            "payment_link": null,
            "subscription": null,
            "invoice": null,
            "tax_id": null,
            "provider_account": null,
            "connected_account": null,
            "application_fee": null,
            "failure_code": null,
            "decline_code": null,
            "metadata": {
              "order_id": "10482"
            },
            "livemode": false,
            "expires_at": 1760086400,
            "paid_at": null,
            "created": 1760000000
          }
        ]
      },
      "PaymentLink": {
        "type": "object",
        "description": "Link do płatności (plink_): stały adres, który można wysłać mailem, wkleić na stronę albo wydrukować jako kod QR. Każde opłacenie tworzy osobną płatność.",
        "required": [
          "id",
          "object",
          "url",
          "currency",
          "active",
          "metadata",
          "livemode",
          "created"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Identyfikator linku.",
            "pattern": "^plink_[A-Za-z0-9_]+$",
            "examples": [
              "plink_Ab12Cd34Ef56Gh"
            ]
          },
          "object": {
            "type": "string",
            "const": "payment_link",
            "description": "Nazwa typu obiektu, zawsze \"payment_link\"."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Adres strony linku."
          },
          "qr_code_url": {
            "type": "string",
            "format": "uri",
            "description": "Kod QR linku jako PNG."
          },
          "qr_svg_url": {
            "type": "string",
            "format": "uri",
            "description": "Kod QR linku jako SVG."
          },
          "amount": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Kwota w groszach; null, gdy klient sam wpisuje kwotę."
          },
          "customer_chooses_amount": {
            "type": "boolean"
          },
          "currency": {
            "type": "string",
            "enum": [
              "PLN",
              "EUR",
              "USD"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "reusable": {
            "type": "boolean",
            "description": "true: link przyjmuje wiele płatności; false: po pierwszej udanej zapłacie przestaje przyjmować."
          },
          "collect_tax_id": {
            "type": "boolean",
            "description": "Strona linku pyta o NIP (faktura dla firmy)."
          },
          "connected_account": {
            "type": [
              "string",
              "null"
            ]
          },
          "application_fee": {
            "type": [
              "integer",
              "null"
            ]
          },
          "active": {
            "type": "boolean",
            "description": "Wyłączony link pokazuje stronę bez możliwości zapłaty."
          },
          "metadata": {
            "type": "object",
            "description": "Własne pary klucz: wartość (do 50 kluczy, klucz do 40 znaków, wartość tekstowa do 500 znaków). Wracają w obiekcie i w zdarzeniach webhooka. Przy zmianie nowe klucze dochodzą do zapisanych, pusta wartość usuwa klucz, pusty tekst zamiast obiektu czyści wszystko.",
            "additionalProperties": {
              "type": "string",
              "maxLength": 500
            },
            "maxProperties": 50,
            "propertyNames": {
              "maxLength": 40
            },
            "examples": [
              {
                "order_id": "10482"
              }
            ]
          },
          "livemode": {
            "type": "boolean",
            "description": "true dla obiektów produkcyjnych (klucz sk_live_), false dla piaskownicy (sk_test_)."
          },
          "expires_at": {
            "type": [
              "integer",
              "null"
            ]
          },
          "created": {
            "type": "integer",
            "description": "Chwila utworzenia jako znacznik czasu Unix (sekundy, UTC).",
            "examples": [
              1760000000
            ]
          }
        },
        "examples": [
          {
            "id": "plink_Ab12Cd34Ef56Gh",
            "object": "payment_link",
            "url": "https://oplac.pl/Kx7Pq2Mn",
            "qr_code_url": "https://oplac.pl/qr/Kx7Pq2Mn.png",
            "qr_svg_url": "https://oplac.pl/Kx7Pq2Mn/qr.svg",
            "amount": 12900,
            "customer_chooses_amount": false,
            "currency": "PLN",
            "description": "Zamówienie 10482",
            "reusable": false,
            "collect_tax_id": false,
            "connected_account": null,
            "application_fee": null,
            "active": true,
            "metadata": {},
            "livemode": false,
            "expires_at": null,
            "created": 1760000000
          }
        ]
      },
      "Refund": {
        "type": "object",
        "description": "Zwrot (re_) całości albo części płatności. Zwroty częściowe sumują się do kwoty płatności.",
        "required": [
          "id",
          "object",
          "payment",
          "amount",
          "currency",
          "status",
          "metadata",
          "livemode",
          "created"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Identyfikator zwrotu.",
            "pattern": "^re_[A-Za-z0-9_]+$",
            "examples": [
              "re_Ab12Cd34Ef56Gh"
            ]
          },
          "object": {
            "type": "string",
            "const": "refund",
            "description": "Nazwa typu obiektu, zawsze \"refund\"."
          },
          "payment": {
            "type": "string",
            "description": "Płatność (pay_) albo pełny obiekt przy expand[]=payment."
          },
          "amount": {
            "type": "integer",
            "description": "Kwota zwrotu w groszach."
          },
          "currency": {
            "type": "string",
            "enum": [
              "PLN",
              "EUR",
              "USD"
            ]
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "requested_by_customer",
              "duplicate",
              "fraudulent",
              "other",
              null
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "succeeded",
              "failed"
            ],
            "description": "W piaskownicy od razu succeeded. W produkcji dostawca może zwrócić pending; wynik przychodzi zdarzeniem refund.succeeded albo refund.failed."
          },
          "metadata": {
            "type": "object",
            "description": "Własne pary klucz: wartość (do 50 kluczy, klucz do 40 znaków, wartość tekstowa do 500 znaków). Wracają w obiekcie i w zdarzeniach webhooka. Przy zmianie nowe klucze dochodzą do zapisanych, pusta wartość usuwa klucz, pusty tekst zamiast obiektu czyści wszystko.",
            "additionalProperties": {
              "type": "string",
              "maxLength": 500
            },
            "maxProperties": 50,
            "propertyNames": {
              "maxLength": 40
            },
            "examples": [
              {
                "order_id": "10482"
              }
            ]
          },
          "livemode": {
            "type": "boolean",
            "description": "true dla obiektów produkcyjnych (klucz sk_live_), false dla piaskownicy (sk_test_)."
          },
          "created": {
            "type": "integer",
            "description": "Chwila utworzenia jako znacznik czasu Unix (sekundy, UTC).",
            "examples": [
              1760000000
            ]
          }
        },
        "examples": [
          {
            "id": "re_Ab12Cd34Ef56Gh",
            "object": "refund",
            "payment": "pay_Ab12Cd34Ef56Gh",
            "amount": 2900,
            "currency": "PLN",
            "reason": "requested_by_customer",
            "status": "succeeded",
            "metadata": {},
            "livemode": false,
            "created": 1760000000
          }
        ]
      },
      "Customer": {
        "type": "object",
        "description": "Klient sprzedawcy (cus_): łączy płatności, subskrypcje i faktury jednej osoby albo firmy. Metody płatności przechowuje dostawca płatności, nie Oplac. Usunięty klient wraca jako {id, object, deleted: true}.",
        "required": [
          "id",
          "object",
          "livemode"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Identyfikator klienta.",
            "pattern": "^cus_[A-Za-z0-9_]+$",
            "examples": [
              "cus_Ab12Cd34Ef56Gh"
            ]
          },
          "object": {
            "type": "string",
            "const": "customer",
            "description": "Nazwa typu obiektu, zawsze \"customer\"."
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "tax_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "NIP."
          },
          "address": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "line1": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "line2": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "city": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "postal_code": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "country": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Kod kraju ISO 3166-1 alpha-2, np. PL."
              }
            }
          },
          "payment_methods": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Zawsze pusta lista: zapisane metody płatności żyją u dostawcy płatności."
          },
          "deleted": {
            "type": "boolean",
            "description": "Tylko dla usuniętego klienta."
          },
          "metadata": {
            "type": "object",
            "description": "Własne pary klucz: wartość (do 50 kluczy, klucz do 40 znaków, wartość tekstowa do 500 znaków). Wracają w obiekcie i w zdarzeniach webhooka. Przy zmianie nowe klucze dochodzą do zapisanych, pusta wartość usuwa klucz, pusty tekst zamiast obiektu czyści wszystko.",
            "additionalProperties": {
              "type": "string",
              "maxLength": 500
            },
            "maxProperties": 50,
            "propertyNames": {
              "maxLength": 40
            },
            "examples": [
              {
                "order_id": "10482"
              }
            ]
          },
          "livemode": {
            "type": "boolean",
            "description": "true dla obiektów produkcyjnych (klucz sk_live_), false dla piaskownicy (sk_test_)."
          },
          "created": {
            "type": "integer",
            "description": "Chwila utworzenia jako znacznik czasu Unix (sekundy, UTC).",
            "examples": [
              1760000000
            ]
          }
        },
        "examples": [
          {
            "id": "cus_Xy98Zw76Vu54Ts",
            "object": "customer",
            "email": "anna@example.com",
            "name": "Anna Nowak",
            "tax_id": null,
            "address": {
              "line1": "ul. Prosta 1",
              "city": "Warszawa",
              "postal_code": "00-001",
              "country": "PL"
            },
            "payment_methods": [],
            "metadata": {
              "crm_id": "7"
            },
            "livemode": false,
            "created": 1760000000
          }
        ]
      },
      "Plan": {
        "type": "object",
        "description": "Plan abonamentowy (plan_) dla klientów sprzedawcy (moduł Bill, od planu Firma). Kwota, waluta i okres są stałe po utworzeniu.",
        "required": [
          "id",
          "object",
          "name",
          "amount",
          "currency",
          "interval",
          "active",
          "metadata",
          "livemode",
          "created"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Identyfikator planu.",
            "pattern": "^plan_[A-Za-z0-9_]+$",
            "examples": [
              "plan_Ab12Cd34Ef56Gh"
            ]
          },
          "object": {
            "type": "string",
            "const": "plan",
            "description": "Nazwa typu obiektu, zawsze \"plan\"."
          },
          "name": {
            "type": "string",
            "maxLength": 120
          },
          "amount": {
            "type": "integer",
            "description": "Kwota okresu w groszach."
          },
          "currency": {
            "type": "string",
            "enum": [
              "PLN",
              "EUR",
              "USD"
            ]
          },
          "interval": {
            "type": "string",
            "enum": [
              "month",
              "year"
            ]
          },
          "trial_days": {
            "type": "integer",
            "description": "Dni okresu próbnego bez obciążenia (0 do 365)."
          },
          "active": {
            "type": "boolean",
            "description": "Wyłączony plan nie przyjmuje nowych subskrypcji; trwające odnawiają się dalej."
          },
          "metadata": {
            "type": "object",
            "description": "Własne pary klucz: wartość (do 50 kluczy, klucz do 40 znaków, wartość tekstowa do 500 znaków). Wracają w obiekcie i w zdarzeniach webhooka. Przy zmianie nowe klucze dochodzą do zapisanych, pusta wartość usuwa klucz, pusty tekst zamiast obiektu czyści wszystko.",
            "additionalProperties": {
              "type": "string",
              "maxLength": 500
            },
            "maxProperties": 50,
            "propertyNames": {
              "maxLength": 40
            },
            "examples": [
              {
                "order_id": "10482"
              }
            ]
          },
          "livemode": {
            "type": "boolean",
            "description": "true dla obiektów produkcyjnych (klucz sk_live_), false dla piaskownicy (sk_test_)."
          },
          "created": {
            "type": "integer",
            "description": "Chwila utworzenia jako znacznik czasu Unix (sekundy, UTC).",
            "examples": [
              1760000000
            ]
          }
        },
        "examples": [
          {
            "id": "plan_Ab12Cd34Ef56Gh",
            "object": "plan",
            "name": "Pro miesięcznie",
            "amount": 4900,
            "currency": "PLN",
            "interval": "month",
            "trial_days": 14,
            "active": true,
            "metadata": {},
            "livemode": false,
            "created": 1760000000
          }
        ]
      },
      "Coupon": {
        "type": "object",
        "description": "Kupon rabatowy (coupon_) do subskrypcji: procent albo kwota, na pierwszy okres, na stałe albo przez kilka miesięcy.",
        "required": [
          "id",
          "object",
          "code",
          "duration",
          "active",
          "metadata",
          "livemode",
          "created"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Identyfikator kuponu.",
            "pattern": "^coupon_[A-Za-z0-9_]+$",
            "examples": [
              "coupon_Ab12Cd34Ef56Gh"
            ]
          },
          "object": {
            "type": "string",
            "const": "coupon",
            "description": "Nazwa typu obiektu, zawsze \"coupon\"."
          },
          "code": {
            "type": "string",
            "description": "Kod wpisywany przez klienta; wielkimi literami."
          },
          "percent_off": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "maximum": 100
          },
          "amount_off": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Kwota rabatu w groszach."
          },
          "currency": {
            "type": [
              "string",
              "null"
            ],
            "description": "Waluta kwoty rabatu; kupon kwotowy pasuje tylko do planów w tej walucie."
          },
          "duration": {
            "type": "string",
            "enum": [
              "once",
              "forever",
              "repeating"
            ]
          },
          "duration_in_months": {
            "type": [
              "integer",
              "null"
            ]
          },
          "max_redemptions": {
            "type": [
              "integer",
              "null"
            ]
          },
          "times_redeemed": {
            "type": "integer"
          },
          "active": {
            "type": "boolean"
          },
          "metadata": {
            "type": "object",
            "description": "Własne pary klucz: wartość (do 50 kluczy, klucz do 40 znaków, wartość tekstowa do 500 znaków). Wracają w obiekcie i w zdarzeniach webhooka. Przy zmianie nowe klucze dochodzą do zapisanych, pusta wartość usuwa klucz, pusty tekst zamiast obiektu czyści wszystko.",
            "additionalProperties": {
              "type": "string",
              "maxLength": 500
            },
            "maxProperties": 50,
            "propertyNames": {
              "maxLength": 40
            },
            "examples": [
              {
                "order_id": "10482"
              }
            ]
          },
          "livemode": {
            "type": "boolean",
            "description": "true dla obiektów produkcyjnych (klucz sk_live_), false dla piaskownicy (sk_test_)."
          },
          "created": {
            "type": "integer",
            "description": "Chwila utworzenia jako znacznik czasu Unix (sekundy, UTC).",
            "examples": [
              1760000000
            ]
          }
        },
        "examples": [
          {
            "id": "coupon_Ab12Cd34Ef56",
            "object": "coupon",
            "code": "START20",
            "percent_off": 20,
            "amount_off": null,
            "currency": null,
            "duration": "once",
            "duration_in_months": null,
            "max_redemptions": 100,
            "times_redeemed": 3,
            "active": true,
            "metadata": {},
            "livemode": false,
            "created": 1760000000
          }
        ]
      },
      "Subscription": {
        "type": "object",
        "description": "Subskrypcja (sub_) klienta sprzedawcy. Po utworzeniu ma status incomplete i checkout_url (strona zapisu); po opłaceniu pierwszego okresu active (albo trialing przy okresie próbnym). Nieudane odnowienie daje past_due i uruchamia Recover.",
        "required": [
          "id",
          "object",
          "plan",
          "status",
          "checkout_url",
          "metadata",
          "livemode",
          "created"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Identyfikator subskrypcji.",
            "pattern": "^sub_[A-Za-z0-9_]+$",
            "examples": [
              "sub_Ab12Cd34Ef56Gh"
            ]
          },
          "object": {
            "type": "string",
            "const": "subscription",
            "description": "Nazwa typu obiektu, zawsze \"subscription\"."
          },
          "plan": {
            "type": "string",
            "description": "Plan (plan_) albo pełny obiekt przy expand[]=plan."
          },
          "status": {
            "type": "string",
            "enum": [
              "incomplete",
              "trialing",
              "active",
              "past_due",
              "canceled"
            ]
          },
          "customer": {
            "type": [
              "string",
              "null"
            ],
            "description": "Klient (cus_) albo obiekt przy expand[]=customer."
          },
          "customer_email": {
            "type": [
              "string",
              "null"
            ]
          },
          "customer_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "tax_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "coupon": {
            "type": [
              "string",
              "null"
            ]
          },
          "amount": {
            "type": "integer",
            "description": "Kwota okresu z planu, przed rabatem."
          },
          "currency": {
            "type": "string"
          },
          "interval": {
            "type": "string",
            "enum": [
              "month",
              "year"
            ]
          },
          "trial_end": {
            "type": [
              "integer",
              "null"
            ]
          },
          "current_period_start": {
            "type": [
              "integer",
              "null"
            ]
          },
          "current_period_end": {
            "type": [
              "integer",
              "null"
            ]
          },
          "cancel_at_period_end": {
            "type": "boolean",
            "description": "true: subskrypcja kończy się po bieżącym opłaconym okresie."
          },
          "canceled_at": {
            "type": [
              "integer",
              "null"
            ]
          },
          "credit_balance": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Kredyt z proracji w groszach, odliczany od następnego odnowienia."
          },
          "last_decline_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Kod ostatniej odmowy przy odnowieniu (insufficient_funds, stolen_card, card_declined)."
          },
          "checkout_url": {
            "type": "string",
            "format": "uri",
            "description": "Strona zapisu klienta (pierwsza płatność, zapis karty u dostawcy)."
          },
          "proration": {
            "type": "object",
            "description": "Tylko w odpowiedzi na zmianę planu: charged (dopłata pobrana teraz) i credited (kredyt na następny okres), w groszach.",
            "properties": {
              "charged": {
                "type": "integer"
              },
              "credited": {
                "type": "integer"
              }
            }
          },
          "metadata": {
            "type": "object",
            "description": "Własne pary klucz: wartość (do 50 kluczy, klucz do 40 znaków, wartość tekstowa do 500 znaków). Wracają w obiekcie i w zdarzeniach webhooka. Przy zmianie nowe klucze dochodzą do zapisanych, pusta wartość usuwa klucz, pusty tekst zamiast obiektu czyści wszystko.",
            "additionalProperties": {
              "type": "string",
              "maxLength": 500
            },
            "maxProperties": 50,
            "propertyNames": {
              "maxLength": 40
            },
            "examples": [
              {
                "order_id": "10482"
              }
            ]
          },
          "livemode": {
            "type": "boolean",
            "description": "true dla obiektów produkcyjnych (klucz sk_live_), false dla piaskownicy (sk_test_)."
          },
          "created": {
            "type": "integer",
            "description": "Chwila utworzenia jako znacznik czasu Unix (sekundy, UTC).",
            "examples": [
              1760000000
            ]
          }
        },
        "examples": [
          {
            "id": "sub_Ab12Cd34Ef56Gh",
            "object": "subscription",
            "plan": "plan_Ab12Cd34Ef56Gh",
            "status": "active",
            "customer": "cus_Xy98Zw76Vu54Ts",
            "customer_email": "anna@example.com",
            "customer_name": "Anna Nowak",
            "tax_id": null,
            "coupon": null,
            "amount": 4900,
            "currency": "PLN",
            "interval": "month",
            "trial_end": null,
            "current_period_start": 1760000000,
            "current_period_end": 1762592000,
            "cancel_at_period_end": false,
            "canceled_at": null,
            "credit_balance": 0,
            "last_decline_code": null,
            "checkout_url": "https://oplac.pl/s/Kx7Pq2MnAb12",
            "metadata": {},
            "livemode": false,
            "created": 1760000000
          }
        ]
      },
      "Invoice": {
        "type": "object",
        "description": "Faktura VAT (inv_). type payable: wystawiona z terminem i własną stroną płatności (moduł Invoices); paid: po opłaceniu płatności albo okresu subskrypcji; credit_note: korekta po zwrocie (kwoty ujemne).",
        "required": [
          "id",
          "object",
          "number",
          "type",
          "status",
          "gross",
          "currency",
          "metadata",
          "livemode",
          "created"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Identyfikator faktury.",
            "pattern": "^inv_[A-Za-z0-9_]+$",
            "examples": [
              "inv_Ab12Cd34Ef56Gh"
            ]
          },
          "object": {
            "type": "string",
            "const": "invoice",
            "description": "Nazwa typu obiektu, zawsze \"invoice\"."
          },
          "number": {
            "type": "string",
            "description": "Numer z serii sprzedawcy."
          },
          "type": {
            "type": "string",
            "enum": [
              "payable",
              "paid",
              "credit_note"
            ]
          },
          "corrects": {
            "type": [
              "string",
              "null"
            ],
            "description": "Faktura pierwotna (inv_) dla korekty."
          },
          "refund": {
            "type": [
              "string",
              "null"
            ],
            "description": "Zwrot (re_), po którym powstała korekta."
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "partially_paid",
              "paid",
              "refunded"
            ]
          },
          "customer_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "customer_tax_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "customer_email": {
            "type": [
              "string",
              "null"
            ]
          },
          "lines": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "nazwa": {
                  "type": "string"
                },
                "ilosc": {
                  "type": "number"
                },
                "netto": {
                  "type": "integer"
                }
              }
            }
          },
          "vat_rate": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Stawka VAT w procentach."
          },
          "net": {
            "type": "integer"
          },
          "vat": {
            "type": "integer"
          },
          "gross": {
            "type": "integer",
            "description": "Kwota brutto w groszach."
          },
          "amount_paid": {
            "type": "integer"
          },
          "amount_due": {
            "type": "integer"
          },
          "overpaid": {
            "type": "integer"
          },
          "currency": {
            "type": "string"
          },
          "issue_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "due_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "disputed": {
            "type": "boolean"
          },
          "payment": {
            "type": [
              "string",
              "null"
            ],
            "description": "Płatność (pay_), która opłaciła fakturę."
          },
          "payment_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Strona płatności faktury (type payable)."
          },
          "ksef_status": {
            "type": [
              "string",
              "null"
            ],
            "description": "Stan w KSeF: null, wyslana, przyjeta, blad."
          },
          "ksef_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "metadata": {
            "type": "object",
            "description": "Własne pary klucz: wartość (do 50 kluczy, klucz do 40 znaków, wartość tekstowa do 500 znaków). Wracają w obiekcie i w zdarzeniach webhooka. Przy zmianie nowe klucze dochodzą do zapisanych, pusta wartość usuwa klucz, pusty tekst zamiast obiektu czyści wszystko.",
            "additionalProperties": {
              "type": "string",
              "maxLength": 500
            },
            "maxProperties": 50,
            "propertyNames": {
              "maxLength": 40
            },
            "examples": [
              {
                "order_id": "10482"
              }
            ]
          },
          "livemode": {
            "type": "boolean",
            "description": "true dla obiektów produkcyjnych (klucz sk_live_), false dla piaskownicy (sk_test_)."
          },
          "paid_at": {
            "type": [
              "integer",
              "null"
            ]
          },
          "created": {
            "type": "integer",
            "description": "Chwila utworzenia jako znacznik czasu Unix (sekundy, UTC).",
            "examples": [
              1760000000
            ]
          }
        },
        "examples": [
          {
            "id": "inv_Ab12Cd34Ef56Gh",
            "object": "invoice",
            "number": "FV/2026/10/0001",
            "type": "payable",
            "corrects": null,
            "refund": null,
            "status": "open",
            "customer_name": "Firma Testowa sp. z o.o.",
            "customer_tax_id": "1234563218",
            "customer_email": "faktury@example.com",
            "lines": [
              {
                "nazwa": "Usługa wdrożenia",
                "ilosc": 1,
                "netto": 100000
              }
            ],
            "vat_rate": 23,
            "net": 100000,
            "vat": 23000,
            "gross": 123000,
            "amount_paid": 0,
            "amount_due": 123000,
            "overpaid": 0,
            "currency": "PLN",
            "issue_date": "2026-10-03",
            "due_date": "2026-10-17",
            "disputed": false,
            "payment": null,
            "payment_url": "https://oplac.pl/f/Kx7Pq2MnAb12",
            "ksef_status": null,
            "ksef_number": null,
            "metadata": {},
            "livemode": false,
            "paid_at": null,
            "created": 1760000000
          }
        ]
      },
      "Product": {
        "type": "object",
        "description": "Produkt (prod_) sprzedawany na stronie sprzedaży bez własnego sklepu: jednorazowy (one_time) albo subskrypcja (recurring) z planem Bill, który obciąża klienta co miesiąc albo co rok. Cena, waluta i typ są stałe po utworzeniu. Zdjęcie dodaje się w panelu; API zwraca image_url.",
        "required": [
          "id",
          "object",
          "name",
          "amount",
          "currency",
          "type",
          "active",
          "metadata",
          "livemode",
          "created"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Identyfikator produktu.",
            "pattern": "^prod_[A-Za-z0-9_]+$",
            "examples": [
              "prod_Ab12Cd34Ef56Gh"
            ]
          },
          "object": {
            "type": "string",
            "const": "product",
            "description": "Nazwa typu obiektu, zawsze \"product\"."
          },
          "name": {
            "type": "string",
            "maxLength": 120
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 2000,
            "description": "Zwykły tekst bez HTML, pokazywany na stronie sprzedaży."
          },
          "image_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Zdjęcie produktu (JPEG, PNG albo WebP) wgrane w panelu; null bez zdjęcia."
          },
          "amount": {
            "type": "integer",
            "description": "Cena w groszach.",
            "examples": [
              12900
            ]
          },
          "currency": {
            "type": "string",
            "enum": [
              "PLN",
              "EUR",
              "USD"
            ]
          },
          "type": {
            "type": "string",
            "enum": [
              "one_time",
              "recurring"
            ],
            "description": "one_time: płatność jednorazowa (Accept); recurring: subskrypcja (Bill, od planu Firma)."
          },
          "interval": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "month",
              "year",
              null
            ],
            "description": "Okres subskrypcji; null dla one_time."
          },
          "plan": {
            "type": [
              "string",
              "null"
            ],
            "description": "Plan Bill (plan_) utworzony razem z produktem recurring; pełny obiekt przy expand[]=plan."
          },
          "active": {
            "type": "boolean",
            "description": "Wyłączony produkt nie przyjmuje zakupów na żadnej stronie sprzedaży."
          },
          "metadata": {
            "type": "object",
            "description": "Własne pary klucz: wartość (do 50 kluczy, klucz do 40 znaków, wartość tekstowa do 500 znaków). Wracają w obiekcie i w zdarzeniach webhooka. Przy zmianie nowe klucze dochodzą do zapisanych, pusta wartość usuwa klucz, pusty tekst zamiast obiektu czyści wszystko.",
            "additionalProperties": {
              "type": "string",
              "maxLength": 500
            },
            "maxProperties": 50,
            "propertyNames": {
              "maxLength": 40
            },
            "examples": [
              {
                "order_id": "10482"
              }
            ]
          },
          "livemode": {
            "type": "boolean",
            "description": "true dla obiektów produkcyjnych (klucz sk_live_), false dla piaskownicy (sk_test_)."
          },
          "created": {
            "type": "integer",
            "description": "Chwila utworzenia jako znacznik czasu Unix (sekundy, UTC).",
            "examples": [
              1760000000
            ]
          }
        },
        "examples": [
          {
            "id": "prod_Ab12Cd34Ef56Gh",
            "object": "product",
            "name": "Kurs fotografii online",
            "description": "Dostęp do nagrań na rok.",
            "image_url": "https://oplac.pl/media/produkt/prod_Ab12Cd34Ef56Gh",
            "amount": 12900,
            "currency": "PLN",
            "type": "one_time",
            "interval": null,
            "plan": null,
            "active": true,
            "metadata": {
              "sku": "KURS-1"
            },
            "livemode": false,
            "created": 1760000000
          }
        ]
      },
      "CheckoutPage": {
        "type": "object",
        "description": "Strona sprzedaży (cpage_): publiczny adres https://oplac.pl/k/<kod> w marce sprzedawcy (kolor i logo z panelu), na którym klient kupuje produkt bez sklepu. Zakup to zwykła płatność (pay_) albo subskrypcja (sub_) z tymi samymi zdarzeniami, fakturami i webhookami co w API; metadata płatności ma checkout_page i product. Kod QR, przycisk HTML do wklejenia i statystyki w obiekcie.",
        "required": [
          "id",
          "object",
          "product",
          "url",
          "qr_code_url",
          "button_html",
          "fields",
          "quantity_max",
          "active",
          "stats",
          "metadata",
          "livemode",
          "created"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Identyfikator strony sprzedaży.",
            "pattern": "^cpage_[A-Za-z0-9_]+$",
            "examples": [
              "cpage_Ab12Cd34Ef56Gh"
            ]
          },
          "object": {
            "type": "string",
            "const": "checkout_page",
            "description": "Nazwa typu obiektu, zawsze \"checkout_page\"."
          },
          "product": {
            "type": "string",
            "description": "Produkt (prod_) albo pełny obiekt przy expand[]=product."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Adres strony sprzedaży do wysłania klientom (Instagram, Facebook, SMS, e-mail)."
          },
          "qr_code_url": {
            "type": "string",
            "format": "uri",
            "description": "Kod QR strony jako PNG."
          },
          "button_html": {
            "type": "string",
            "description": "Gotowy przycisk „Kup teraz” (zwykły link bez skryptów) do wklejenia na dowolną stronę."
          },
          "fields": {
            "type": "object",
            "description": "Pola dla kupującego poza e-mailem i nazwą (zawsze obecne): address (adres), tax_id (NIP do faktury), phone (telefon), coupon (kod rabatowy z kuponów Bill, od planu Firma).",
            "properties": {
              "address": {
                "type": "boolean"
              },
              "tax_id": {
                "type": "boolean"
              },
              "phone": {
                "type": "boolean"
              },
              "coupon": {
                "type": "boolean"
              }
            }
          },
          "quantity_max": {
            "type": "integer",
            "minimum": 1,
            "maximum": 99,
            "description": "Największa ilość w jednym zamówieniu; 1 to bez wyboru ilości. Subskrypcja zawsze 1."
          },
          "success_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Adres, na który klient trafia po zapłacie (z ?payment=pay_... albo ?subscription=sub_...); null to strona podziękowania Oplac."
          },
          "message": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 1000,
            "description": "Komunikat na stronie podziękowania (gdy nie ma success_url)."
          },
          "active": {
            "type": "boolean",
            "description": "Wyłączona strona pokazuje informację o wstrzymanej sprzedaży."
          },
          "stats": {
            "type": "object",
            "description": "Statystyki strony: views (wyświetlenia), payments (opłacone płatności, także okresy subskrypcji), revenue (przychód brutto w groszach).",
            "properties": {
              "views": {
                "type": "integer"
              },
              "payments": {
                "type": "integer"
              },
              "revenue": {
                "type": "integer"
              }
            }
          },
          "metadata": {
            "type": "object",
            "description": "Własne pary klucz: wartość (do 50 kluczy, klucz do 40 znaków, wartość tekstowa do 500 znaków). Wracają w obiekcie i w zdarzeniach webhooka. Przy zmianie nowe klucze dochodzą do zapisanych, pusta wartość usuwa klucz, pusty tekst zamiast obiektu czyści wszystko.",
            "additionalProperties": {
              "type": "string",
              "maxLength": 500
            },
            "maxProperties": 50,
            "propertyNames": {
              "maxLength": 40
            },
            "examples": [
              {
                "order_id": "10482"
              }
            ]
          },
          "livemode": {
            "type": "boolean",
            "description": "true dla obiektów produkcyjnych (klucz sk_live_), false dla piaskownicy (sk_test_)."
          },
          "created": {
            "type": "integer",
            "description": "Chwila utworzenia jako znacznik czasu Unix (sekundy, UTC).",
            "examples": [
              1760000000
            ]
          }
        },
        "examples": [
          {
            "id": "cpage_Ab12Cd34Ef56Gh",
            "object": "checkout_page",
            "product": "prod_Ab12Cd34Ef56Gh",
            "url": "https://oplac.pl/k/Kx7Pq2MnAb12",
            "qr_code_url": "https://oplac.pl/k/Kx7Pq2MnAb12/qr.png",
            "button_html": "<a href=\"https://oplac.pl/k/Kx7Pq2MnAb12\" style=\"display:inline-block;background:#0A1F44;color:#fff;padding:12px 22px;border-radius:6px;font:600 15px Inter,Arial,sans-serif;text-decoration:none\">Kup teraz</a>",
            "fields": {
              "address": false,
              "tax_id": true,
              "phone": false,
              "coupon": false
            },
            "quantity_max": 1,
            "success_url": null,
            "message": "Dostęp wyślemy w ciągu godziny.",
            "active": true,
            "stats": {
              "views": 318,
              "payments": 24,
              "revenue": 309600
            },
            "metadata": {},
            "livemode": false,
            "created": 1760000000
          }
        ]
      },
      "PortalSession": {
        "type": "object",
        "description": "Sesja portalu klienta (bps_): jednorazowy adres (url), pod którym klient końcowy sprzedawcy wchodzi bez hasła do swoich płatności, faktur PDF i subskrypcji (nowa karta, rezygnacja z końcem okresu, zmiana planu, dane do faktury; zakres zmian ustawia sprzedawca w panelu). Adres działa godzinę i raz; po wejściu sesja trwa dwie godziny. url wraca wyłącznie przy utworzeniu.",
        "required": [
          "id",
          "object",
          "url",
          "livemode",
          "created"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Identyfikator sesji.",
            "pattern": "^bps_[A-Za-z0-9_]+$",
            "examples": [
              "bps_Ab12Cd34Ef56Gh"
            ]
          },
          "object": {
            "type": "string",
            "const": "portal_session",
            "description": "Nazwa typu obiektu, zawsze \"portal_session\"."
          },
          "customer": {
            "type": [
              "string",
              "null"
            ],
            "description": "Klient (cus_) albo pełny obiekt przy expand[]=customer."
          },
          "customer_email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email"
          },
          "url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Jednorazowy adres wejścia do portalu (tylko w odpowiedzi na utworzenie)."
          },
          "return_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "expires_at": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Do kiedy działa adres wejścia (Unix)."
          },
          "livemode": {
            "type": "boolean",
            "description": "true dla obiektów produkcyjnych (klucz sk_live_), false dla piaskownicy (sk_test_)."
          },
          "created": {
            "type": "integer",
            "description": "Chwila utworzenia jako znacznik czasu Unix (sekundy, UTC).",
            "examples": [
              1760000000
            ]
          }
        },
        "examples": [
          {
            "id": "bps_Ab12Cd34Ef56Gh",
            "object": "portal_session",
            "customer": "cus_Xy98Zw76Vu54Ts",
            "customer_email": "anna@example.com",
            "url": "https://oplac.pl/portal/wejscie/Ab12Cd34Ef56Gh78Ij90Kl12Mn34Op56Qr78St90Uv12Wx34",
            "return_url": "https://app.example.com/konto",
            "expires_at": 1760003600,
            "livemode": false,
            "created": 1760000000
          }
        ]
      },
      "Event": {
        "type": "object",
        "description": "Zdarzenie (evt_). Ta sama treść idzie na webhook (bez pola delivery) i jest dostępna przez GET /v1/events. data.object to migawka obiektu z chwili zdarzenia.",
        "required": [
          "id",
          "object",
          "type",
          "api_version",
          "livemode",
          "created",
          "data",
          "pending_webhooks"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Identyfikator zdarzenia.",
            "pattern": "^evt_[A-Za-z0-9_]+$",
            "examples": [
              "evt_Ab12Cd34Ef56Gh"
            ]
          },
          "object": {
            "type": "string",
            "const": "event",
            "description": "Nazwa typu obiektu, zawsze \"event\"."
          },
          "type": {
            "type": "string",
            "enum": [
              "payment.created",
              "payment.succeeded",
              "payment.failed",
              "payment.canceled",
              "payment.routed",
              "payment_link.expired",
              "account.verified",
              "payout.paid",
              "refund.created",
              "refund.succeeded",
              "refund.failed",
              "customer.created",
              "customer.updated",
              "customer.deleted",
              "subscription.created",
              "subscription.updated",
              "subscription.renewed",
              "subscription.payment_failed",
              "subscription.recovered",
              "subscription.canceled",
              "invoice.issued",
              "invoice.paid",
              "invoice.ksef_accepted",
              "invoice.ksef_rejected"
            ]
          },
          "api_version": {
            "type": "string",
            "examples": [
              "2026-10-03"
            ]
          },
          "livemode": {
            "type": "boolean",
            "description": "true dla obiektów produkcyjnych (klucz sk_live_), false dla piaskownicy (sk_test_)."
          },
          "created": {
            "type": "integer",
            "description": "Chwila utworzenia jako znacznik czasu Unix (sekundy, UTC).",
            "examples": [
              1760000000
            ]
          },
          "data": {
            "type": "object",
            "required": [
              "object"
            ],
            "properties": {
              "object": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/Payment"
                  },
                  {
                    "$ref": "#/components/schemas/PaymentLink"
                  },
                  {
                    "$ref": "#/components/schemas/Refund"
                  },
                  {
                    "$ref": "#/components/schemas/Customer"
                  },
                  {
                    "$ref": "#/components/schemas/Subscription"
                  },
                  {
                    "$ref": "#/components/schemas/Invoice"
                  },
                  {
                    "type": "object"
                  }
                ]
              }
            }
          },
          "request": {
            "type": [
              "object",
              "null"
            ],
            "description": "Żądanie API, które wywołało zdarzenie; null dla zdarzeń z harmonogramu, strony płatności i panelu.",
            "properties": {
              "id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "idempotency_key": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "pending_webhooks": {
            "type": "integer",
            "description": "1, gdy webhook jeszcze nie dotarł i czeka na ponowienie; 0 po doręczeniu albo bez adresu webhooka."
          },
          "delivery": {
            "type": "object",
            "description": "Stan doręczenia (tylko w API, nie w treści webhooka).",
            "properties": {
              "attempts": {
                "type": "integer"
              },
              "last_status": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "Kod HTTP ostatniej odpowiedzi serwera odbiorcy."
              },
              "delivered_at": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "next_attempt": {
                "type": [
                  "integer",
                  "null"
                ]
              }
            }
          }
        },
        "examples": [
          {
            "id": "evt_Ab12Cd34Ef56Gh",
            "object": "event",
            "type": "payment.succeeded",
            "api_version": "2026-10-03",
            "livemode": false,
            "created": 1760000000,
            "data": {
              "object": {
                "id": "pay_Ab12Cd34Ef56Gh",
                "object": "payment",
                "amount": 12900,
                "amount_refunded": 0,
                "currency": "PLN",
                "status": "succeeded",
                "method": "blik",
                "description": "Zamówienie 10482",
                "checkout_url": "https://oplac.pl/p/Ab12Cd34Ef56Gh",
                "return_url": "https://sklep.example.com/powrot",
                "customer": "cus_Xy98Zw76Vu54Ts",
                "customer_email": "anna@example.com",
                "customer_name": "Anna Nowak",
                "payment_link": null,
                "subscription": null,
                "invoice": null,
                "tax_id": null,
                "provider_account": null,
                "connected_account": null,
                "application_fee": null,
                "failure_code": null,
                "decline_code": null,
                "metadata": {
                  "order_id": "10482"
                },
                "livemode": false,
                "expires_at": 1760086400,
                "paid_at": 1760000000,
                "created": 1760000000
              }
            },
            "request": {
              "id": null,
              "idempotency_key": null
            },
            "pending_webhooks": 0,
            "delivery": {
              "attempts": 1,
              "last_status": 200,
              "delivered_at": 1760000001,
              "next_attempt": null
            }
          }
        ]
      },
      "WebhookEndpoint": {
        "type": "object",
        "description": "Adres webhooka (we_) trybu klucza: jeden na tryb. Sekret whsec_ wraca tylko przy utworzeniu i przy rotate_secret.",
        "required": [
          "id",
          "object",
          "url",
          "enabled_events",
          "status",
          "livemode"
        ],
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "we_test_Ab12Cd34Ef56Gh"
            ]
          },
          "object": {
            "type": "string",
            "const": "webhook_endpoint",
            "description": "Nazwa typu obiektu, zawsze \"webhook_endpoint\"."
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "enabled_events": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Lista typów zdarzeń albo [\"*\"] dla wszystkich."
          },
          "status": {
            "type": "string",
            "enum": [
              "enabled"
            ]
          },
          "secret": {
            "type": "string",
            "description": "Sekret podpisu whsec_ (tylko przy utworzeniu i rotacji)."
          },
          "livemode": {
            "type": "boolean",
            "description": "true dla obiektów produkcyjnych (klucz sk_live_), false dla piaskownicy (sk_test_)."
          },
          "created": {
            "type": "integer",
            "description": "Chwila utworzenia jako znacznik czasu Unix (sekundy, UTC).",
            "examples": [
              1760000000
            ]
          }
        },
        "examples": [
          {
            "id": "we_test_Ab12Cd34Ef56Gh",
            "object": "webhook_endpoint",
            "url": "https://sklep.example.com/oplac/webhook",
            "enabled_events": [
              "payment.succeeded",
              "payment.failed",
              "refund.succeeded"
            ],
            "status": "enabled",
            "secret": "whsec_Ab12Cd34Ef56Gh78Ij90Kl12Mn34Op56Qr78St90",
            "livemode": false,
            "created": 1760000000
          }
        ]
      },
      "Deleted": {
        "type": "object",
        "required": [
          "id",
          "object",
          "deleted"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string"
          },
          "deleted": {
            "type": "boolean",
            "const": true
          },
          "livemode": {
            "type": "boolean",
            "description": "true dla obiektów produkcyjnych (klucz sk_live_), false dla piaskownicy (sk_test_)."
          }
        }
      }
    }
  },
  "webhooks": {
    "event": {
      "post": {
        "summary": "Zdarzenie na adres webhooka",
        "description": "POST na zapisany adres z nagłówkami Content-Type: application/json, User-Agent: Oplac-Webhooks/1.0 i Oplac-Signature: t=<unix>,v1=<hmac_sha256(sekret, \"<t>.<treść>\")>. Odpowiedz 2xx; inaczej ponowienia wykładnicze przez 3 doby.",
        "parameters": [
          {
            "name": "Oplac-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^t=\\d+,v1=[a-f0-9]{64}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Event"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Doręczone."
          }
        }
      }
    }
  },
  "x-oplac-error-codes": [
    {
      "code": "parameter_missing",
      "type": "invalid_request_error",
      "status": 400,
      "description": "Brakuje wymaganego pola. Pole `param` mówi którego.",
      "doc_url": "https://oplac.pl/docs/bledy#parameter_missing"
    },
    {
      "code": "parameter_invalid",
      "type": "invalid_request_error",
      "status": 400,
      "description": "Pole ma złą wartość albo zły typ. Pole `param` mówi które, `message` jak poprawić.",
      "doc_url": "https://oplac.pl/docs/bledy#parameter_invalid"
    },
    {
      "code": "parameter_unknown",
      "type": "invalid_request_error",
      "status": 400,
      "description": "Żądanie zawiera pole, którego ten zasób nie przyjmuje.",
      "doc_url": "https://oplac.pl/docs/bledy#parameter_unknown"
    },
    {
      "code": "parameter_unsupported",
      "type": "invalid_request_error",
      "status": 400,
      "description": "Pole istnieje, ale nie jest obsługiwane w tym planie albo trybie.",
      "doc_url": "https://oplac.pl/docs/bledy#parameter_unsupported"
    },
    {
      "code": "metadata_invalid",
      "type": "invalid_request_error",
      "status": 400,
      "description": "Metadata to do 50 kluczy, klucz do 40 znaków, wartość tekstowa do 500 znaków.",
      "doc_url": "https://oplac.pl/docs/bledy#metadata_invalid"
    },
    {
      "code": "expand_invalid",
      "type": "invalid_request_error",
      "status": 400,
      "description": "Pole w `expand[]` nie jest rozwijalne w tym zasobie.",
      "doc_url": "https://oplac.pl/docs/bledy#expand_invalid"
    },
    {
      "code": "api_version_invalid",
      "type": "invalid_request_error",
      "status": 400,
      "description": "Nagłówek Oplac-Version wskazuje nieznaną wersję API.",
      "doc_url": "https://oplac.pl/docs/bledy#api_version_invalid"
    },
    {
      "code": "url_not_found",
      "type": "invalid_request_error",
      "status": 404,
      "description": "Nie ma takiej trasy API. Sprawdź ścieżkę i wersję (/v1).",
      "doc_url": "https://oplac.pl/docs/bledy#url_not_found"
    },
    {
      "code": "method_not_allowed",
      "type": "invalid_request_error",
      "status": 405,
      "description": "Ta trasa nie obsługuje tej metody HTTP. Nagłówek Allow wymienia dozwolone.",
      "doc_url": "https://oplac.pl/docs/bledy#method_not_allowed"
    },
    {
      "code": "resource_missing",
      "type": "invalid_request_error",
      "status": 404,
      "description": "Nie ma obiektu o tym identyfikatorze w tym koncie i trybie (sk_test_ widzi tylko obiekty testowe).",
      "doc_url": "https://oplac.pl/docs/bledy#resource_missing"
    },
    {
      "code": "resource_deleted",
      "type": "invalid_request_error",
      "status": 400,
      "description": "Obiekt został usunięty i nie można go zmieniać.",
      "doc_url": "https://oplac.pl/docs/bledy#resource_deleted"
    },
    {
      "code": "api_key_invalid",
      "type": "authentication_error",
      "status": 401,
      "description": "Brak nagłówka Authorization albo klucz jest nieważny, odwołany lub konto nie ma aktywnego planu.",
      "doc_url": "https://oplac.pl/docs/bledy#api_key_invalid"
    },
    {
      "code": "module_not_in_plan",
      "type": "invalid_request_error",
      "status": 403,
      "description": "Moduł (Bill, Invoices) jest w wyższym planie konta.",
      "doc_url": "https://oplac.pl/docs/bledy#module_not_in_plan"
    },
    {
      "code": "currency_not_in_plan",
      "type": "invalid_request_error",
      "status": 403,
      "description": "Waluty EUR i USD są od planu Skala.",
      "doc_url": "https://oplac.pl/docs/bledy#currency_not_in_plan"
    },
    {
      "code": "connect_not_in_plan",
      "type": "invalid_request_error",
      "status": 403,
      "description": "Podział płatności (Connect) jest w planie Platforma.",
      "doc_url": "https://oplac.pl/docs/bledy#connect_not_in_plan"
    },
    {
      "code": "sandbox_limit_reached",
      "type": "invalid_request_error",
      "status": 403,
      "description": "Piaskownica demo mieści kilka linków testowych. Więcej po wyborze planu.",
      "doc_url": "https://oplac.pl/docs/bledy#sandbox_limit_reached"
    },
    {
      "code": "test_helpers_live_mode",
      "type": "invalid_request_error",
      "status": 403,
      "description": "Pomocnicy testowi działają wyłącznie z kluczem sk_test_.",
      "doc_url": "https://oplac.pl/docs/bledy#test_helpers_live_mode"
    },
    {
      "code": "provider_not_connected",
      "type": "invalid_request_error",
      "status": 402,
      "description": "Konto nie ma podpiętego dostawcy płatności. Podepnij go w panelu, w zakładce Dostawca.",
      "doc_url": "https://oplac.pl/docs/bledy#provider_not_connected"
    },
    {
      "code": "provider_error",
      "type": "api_error",
      "status": 502,
      "description": "Dostawca płatności nie odpowiedział albo odrzucił operację. Ponów żądanie za chwilę.",
      "doc_url": "https://oplac.pl/docs/bledy#provider_error"
    },
    {
      "code": "payment_not_cancelable",
      "type": "invalid_request_error",
      "status": 422,
      "description": "Anulować można tylko płatność w statusie pending albo requires_confirmation.",
      "doc_url": "https://oplac.pl/docs/bledy#payment_not_cancelable"
    },
    {
      "code": "payment_not_confirmable",
      "type": "invalid_request_error",
      "status": 422,
      "description": "Potwierdzić w piaskownicy można tylko płatność otwartą (pending, requires_confirmation).",
      "doc_url": "https://oplac.pl/docs/bledy#payment_not_confirmable"
    },
    {
      "code": "payment_not_refundable",
      "type": "invalid_request_error",
      "status": 422,
      "description": "Zwrot dotyczy tylko płatności w statusie succeeded.",
      "doc_url": "https://oplac.pl/docs/bledy#payment_not_refundable"
    },
    {
      "code": "refund_exceeds_amount",
      "type": "invalid_request_error",
      "status": 422,
      "description": "Suma zwrotów przekroczyłaby kwotę płatności.",
      "doc_url": "https://oplac.pl/docs/bledy#refund_exceeds_amount"
    },
    {
      "code": "payment_link_inactive",
      "type": "invalid_request_error",
      "status": 422,
      "description": "Link do płatności jest wyłączony.",
      "doc_url": "https://oplac.pl/docs/bledy#payment_link_inactive"
    },
    {
      "code": "coupon_invalid",
      "type": "invalid_request_error",
      "status": 400,
      "description": "Kupon nie istnieje, jest wyłączony, wyczerpany albo ma inną walutę niż plan.",
      "doc_url": "https://oplac.pl/docs/bledy#coupon_invalid"
    },
    {
      "code": "subscription_not_active",
      "type": "invalid_request_error",
      "status": 422,
      "description": "Operacja wymaga żywej subskrypcji (trialing, active, past_due).",
      "doc_url": "https://oplac.pl/docs/bledy#subscription_not_active"
    },
    {
      "code": "currency_mismatch",
      "type": "invalid_request_error",
      "status": 400,
      "description": "Waluta nowego planu różni się od waluty subskrypcji.",
      "doc_url": "https://oplac.pl/docs/bledy#currency_mismatch"
    },
    {
      "code": "connected_account_invalid",
      "type": "invalid_request_error",
      "status": 400,
      "description": "Konto sprzedawcy platformy nie należy do tego konta albo nie istnieje.",
      "doc_url": "https://oplac.pl/docs/bledy#connected_account_invalid"
    },
    {
      "code": "connected_account_not_ready",
      "type": "invalid_request_error",
      "status": 400,
      "description": "Sprzedawca platformy nie przeszedł jeszcze weryfikacji u dostawcy.",
      "doc_url": "https://oplac.pl/docs/bledy#connected_account_not_ready"
    },
    {
      "code": "application_fee_invalid",
      "type": "invalid_request_error",
      "status": 400,
      "description": "Prowizja platformy musi być od 0 do kwoty płatności.",
      "doc_url": "https://oplac.pl/docs/bledy#application_fee_invalid"
    },
    {
      "code": "amount_too_large",
      "type": "invalid_request_error",
      "status": 400,
      "description": "Suma faktury przekracza 999 999 999,99.",
      "doc_url": "https://oplac.pl/docs/bledy#amount_too_large"
    },
    {
      "code": "seller_details_missing",
      "type": "invalid_request_error",
      "status": 400,
      "description": "Dane firmy sprzedawcy (nazwa, NIP, adres) są puste. Uzupełnij je w panelu, w Ustawieniach.",
      "doc_url": "https://oplac.pl/docs/bledy#seller_details_missing"
    },
    {
      "code": "webhook_not_configured",
      "type": "invalid_request_error",
      "status": 400,
      "description": "Konto nie ma adresu webhooka trybu testowego. Zapisz go w panelu, w zakładce Deweloperzy.",
      "doc_url": "https://oplac.pl/docs/bledy#webhook_not_configured"
    },
    {
      "code": "product_inactive",
      "type": "invalid_request_error",
      "status": 422,
      "description": "Produkt jest wyłączony (active: false), więc nie można dla niego utworzyć strony sprzedaży.",
      "doc_url": "https://oplac.pl/docs/bledy#product_inactive"
    },
    {
      "code": "checkout_page_inactive",
      "type": "invalid_request_error",
      "status": 422,
      "description": "Strona sprzedaży jest wyłączona albo jej produkt nie przyjmuje zakupów.",
      "doc_url": "https://oplac.pl/docs/bledy#checkout_page_inactive"
    },
    {
      "code": "portal_disabled",
      "type": "invalid_request_error",
      "status": 400,
      "description": "Portal klienta jest wyłączony w panelu, więc sesji portalu nie da się utworzyć.",
      "doc_url": "https://oplac.pl/docs/bledy#portal_disabled"
    },
    {
      "code": "idempotency_key_invalid",
      "type": "idempotency_error",
      "status": 400,
      "description": "Nagłówek Idempotency-Key ma od 1 do 255 znaków.",
      "doc_url": "https://oplac.pl/docs/bledy#idempotency_key_invalid"
    },
    {
      "code": "idempotency_key_reused",
      "type": "idempotency_error",
      "status": 400,
      "description": "Ten Idempotency-Key był już użyty z inną treścią albo na innej trasie. Nowy zamiar to nowy klucz.",
      "doc_url": "https://oplac.pl/docs/bledy#idempotency_key_reused"
    },
    {
      "code": "idempotency_key_in_use",
      "type": "idempotency_error",
      "status": 409,
      "description": "Żądanie z tym Idempotency-Key jest właśnie przetwarzane. Ponów za chwilę, dostaniesz jego wynik.",
      "doc_url": "https://oplac.pl/docs/bledy#idempotency_key_in_use"
    },
    {
      "code": "rate_limited",
      "type": "rate_limit_error",
      "status": 429,
      "description": "Przekroczony limit zapytań konta. Odczekaj tyle sekund, ile podaje Retry-After.",
      "doc_url": "https://oplac.pl/docs/bledy#rate_limited"
    },
    {
      "code": "api_error",
      "type": "api_error",
      "status": 500,
      "description": "Błąd po stronie Oplac. Ponów żądanie; jeśli się powtarza, podaj request_id w zgłoszeniu.",
      "doc_url": "https://oplac.pl/docs/bledy#api_error"
    }
  ],
  "x-oplac-event-types": [
    "payment.created",
    "payment.succeeded",
    "payment.failed",
    "payment.canceled",
    "payment.routed",
    "payment_link.expired",
    "account.verified",
    "payout.paid",
    "refund.created",
    "refund.succeeded",
    "refund.failed",
    "customer.created",
    "customer.updated",
    "customer.deleted",
    "subscription.created",
    "subscription.updated",
    "subscription.renewed",
    "subscription.payment_failed",
    "subscription.recovered",
    "subscription.canceled",
    "invoice.issued",
    "invoice.paid",
    "invoice.ksef_accepted",
    "invoice.ksef_rejected"
  ],
  "x-oplac-sandbox": {
    "blik_codes": {
      "777123": "succeeded",
      "777000": "failed (blik_rejected)",
      "777408": "failed (blik_code_invalid)"
    },
    "card": "Na stronie płatności piaskownicy przyciski „Zapłać” (succeeded) i „Odrzuć” (failed, card_declined); przez API POST /v1/test_helpers/payments/{id}/confirm z outcome.",
    "subscriptions": "POST /v1/test_helpers/subscriptions/{id}/confirm (pierwszy okres) i /renew (odnowienie: succeeded, failed, failed_permanently).",
    "webhooks": "POST /v1/test_helpers/webhooks z type wysyła podpisane zdarzenie próbne na adres testowy."
  }
}
