Dla integratorów

Dokumentacja API podpiszmy.pl

Umowa powstaje w Twoim systemie, my wysyłamy ją do podpisu, a podpisany plik wraca do Ciebie. Bez klikania w panelu.

API i webhooki działają w planie Business oraz Team. Plan wybierzesz na stronie z cennikiem. Token wygenerujesz w panelu, w sekcji API i integracje.

Uwierzytelnianie

Każde zapytanie niesie token osobisty w nagłówku Authorization. Token tworzysz i unieważniasz w panelu, w sekcji API i integracje. Pokazujemy go jeden raz, przy utworzeniu, i nie mamy sposobu, żeby pokazać go ponownie. Token działa wyłącznie na koncie, na którym powstał, i widzi dokładnie te dokumenty, które widzi to konto w przeglądarce.

Odpowiedzi są zawsze w formacie JSON, także przy błędzie. Warto wysyłać nagłówek Accept: application/json, ale nie jest on wymagany.

curl -s https://podpiszmy.pl/api/v1/me \
  -H "Authorization: Bearer TWOJ_TOKEN" \
  -H "Accept: application/json"

Adres bazowy to https://podpiszmy.pl/api/v1. Wersja siedzi w adresie, więc kolejne wydanie API nie zepsuje działającej integracji.

Typowy obieg

Najczęstszy scenariusz wygląda tak. W Twoim systemie powstaje umowa (oferta zaakceptowana, zlecenie przyjęte, nowy współpracownik). Wysyłasz ją jednym zapytaniem, my zajmujemy się resztą, a po komplecie podpisów przychodzi do Ciebie zdarzenie i pobierasz podpisany plik.

  1. Raz, przy wdrożeniu: rejestrujesz adres webhooka i zapisujesz jego sekret.
  2. POST /documents z plikiem PDF i listą sygnatariuszy. Dokument idzie do podpisu od razu.
  3. Dostajesz zdarzenia o kolejnych krokach: wysłany, otwarty, podpisany.
  4. Po komplecie podpisów przychodzi document.completed.
  5. GET /documents/{uuid}/pdf zwraca podpisany plik, a /audyt ślad audytowy do Twojej bazy.

Wysłanie dokumentu do podpisu

Jedno zapytanie tworzy dokument, zaprasza sygnatariuszy i wysyła im wiadomość z linkiem. Plik przesyłasz jako multipart/form-data. Maksymalny rozmiar PDF to 6 MB.

curl -s https://podpiszmy.pl/api/v1/documents \
  -H "Authorization: Bearer TWOJ_TOKEN" \
  -F "[email protected]" \
  -F "title=Umowa o dzieło nr 14/2026" \
  -F "message=W razie pytań proszę o kontakt." \
  -F "signing_mode=sequential" \
  -F "signers[0][name]=Anna Nowak" \
  -F "signers[0][email][email protected]" \
  -F "signers[1][name]=Jan Kowalski" \
  -F "signers[1][email][email protected]"

Pola: file (PDF, wymagane), title (wymagane, do 180 znaków), message (opcjonalna wiadomość do sygnatariuszy), signing_mode (sequential po kolei albo parallel równolegle, domyślnie po kolei), signers (od jednego do piętnastu, każdy z name i email, opcjonalnie require_id_check dla weryfikacji tożsamości), send (domyślnie true; ustaw false, żeby przygotować dokument i wysłać go osobnym zapytaniem).

HTTP/1.1 201 Created

{
  "data": {
    "uuid": "9f1c2d3e-4a5b-6c7d-8e9f-0a1b2c3d4e5f",
    "title": "Umowa o dzieło nr 14/2026",
    "status": "sent",
    "effective_status": "sent",
    "status_label": "Wysłany",
    "signing_mode": "sequential",
    "sent_at": "2026-09-19T12:40:11+02:00",
    "expires_at": "2026-10-03T12:40:11+02:00",
    "signed_count": 0,
    "pdf_url": "https://podpiszmy.pl/api/v1/documents/9f1c2d3e.../pdf",
    "audit_url": "https://podpiszmy.pl/api/v1/documents/9f1c2d3e.../audyt",
    "signers": [
      { "name": "Anna Nowak", "email": "[email protected]", "status": "pending", "signed_at": null }
    ]
  }
}

Punkty końcowe

Metoda i adres Co robi
GET /me Dane konta, plan i obowiązujące limity zapytań.
GET /documents Lista dokumentów. Filtr ?status=draft|sent|in_progress|completed|rejected|cancelled|expired|w_toku, wyszukiwanie ?q=, stronicowanie ?per_page= (do 100).
POST /documents Tworzy dokument i domyślnie od razu wysyła go do podpisu.
GET /documents/{uuid} Status jednego dokumentu razem ze stanem każdego sygnatariusza.
POST /documents/{uuid}/send Wysyła dokument przygotowany wcześniej z send=false.
POST /documents/{uuid}/cancel Anuluje dokument. Wysłane linki do podpisu przestają działać. Opcjonalne pole reason.
POST /documents/{uuid}/reminders Przypomina o podpisie. Bez pola email przypomina wszystkim, na których czekamy.
GET /documents/{uuid}/pdf Podpisany PDF. Parametr ?wariant=oryginal zwraca plik bez podpisów.
GET /documents/{uuid}/audyt Ślad audytowy jako dane, razem z wynikiem sprawdzenia łańcucha skrótów.
GET /webhooks Lista zarejestrowanych adresów i katalog dostępnych zdarzeń.
POST /webhooks Rejestruje adres. Odpowiedź zawiera sekret, pokazywany tylko ten jeden raz.
PATCH /webhooks/{id} Zmienia listę zdarzeń albo wyłącza adres.
DELETE /webhooks/{id} Usuwa adres.
POST /webhooks/{id}/test Wysyła zdarzenie próbne, żeby sprawdzić podpis bez czekania na umowę.
GET /webhooks/{id}/dostarczenia Ostatnie próby dostarczenia razem z kodami odpowiedzi i błędami.
# status jednego dokumentu
curl -s https://podpiszmy.pl/api/v1/documents/UUID \
  -H "Authorization: Bearer TWOJ_TOKEN"

# tylko te, na które jeszcze czekamy
curl -s "https://podpiszmy.pl/api/v1/documents?status=w_toku" \
  -H "Authorization: Bearer TWOJ_TOKEN"

# podpisany plik prosto na dysk
curl -s https://podpiszmy.pl/api/v1/documents/UUID/pdf \
  -H "Authorization: Bearer TWOJ_TOKEN" -o umowa-podpisana.pdf

# ślad audytowy do zapisania u siebie
curl -s https://podpiszmy.pl/api/v1/documents/UUID/audyt \
  -H "Authorization: Bearer TWOJ_TOKEN"

# przypomnienie do jednej osoby
curl -s -X POST https://podpiszmy.pl/api/v1/documents/UUID/reminders \
  -H "Authorization: Bearer TWOJ_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"email":"[email protected]"}'

# anulowanie
curl -s -X POST https://podpiszmy.pl/api/v1/documents/UUID/cancel \
  -H "Authorization: Bearer TWOJ_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"reason":"Zlecenie wycofane przez klienta"}'

Podpisany plik powstaje w chwili złożenia ostatniego podpisu. Wcześniej /pdf odpowiada kodem 409 i mówi, ilu podpisów brakuje, zamiast oddawać plik bez podpisów.

Webhooki

Zamiast odpytywać nas w pętli, podaj adres, na który mamy wysyłać zdarzenia. Wysyłamy je metodą POST, w formacie JSON, wyłącznie na publiczne adresy https.

curl -s -X POST https://podpiszmy.pl/api/v1/webhooks \
  -H "Authorization: Bearer TWOJ_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
        "url": "https://twoj-system.pl/hooki/podpiszmy",
        "events": ["document.sent", "document.signed", "document.completed", "document.rejected"]
      }'
Zdarzenie Kiedy przychodzi
document.sent Dokument wysłany do podpisu
document.viewed Sygnatariusz otworzył dokument
document.signed Jeden z sygnatariuszy podpisał
document.rejected Dokument odrzucony
document.expired Minął termin podpisania
document.cancelled Dokument anulowany przez nadawcę
document.completed Dokument podpisany przez wszystkich

Treść zdarzenia

POST /hooki/podpiszmy
Content-Type: application/json
X-Podpiszmy-Event: document.completed
X-Podpiszmy-Signature: sha256=7b1c...
X-Podpiszmy-Delivery: 2f9a1c40-1d8e-4d21-9f4e-6a0b0c7d2e11
X-Podpiszmy-Attempt: 1

{
  "event": "document.completed",
  "created_at": "2026-09-19T13:02:44+02:00",
  "data": {
    "uuid": "9f1c2d3e-4a5b-6c7d-8e9f-0a1b2c3d4e5f",
    "title": "Umowa o dzieło nr 14/2026",
    "status": "completed",
    "completed_at": "2026-09-19T13:02:41+02:00",
    "signers": [
      { "name": "Anna Nowak", "email": "[email protected]", "status": "signed", "signed_at": "2026-09-19T13:02:41+02:00" }
    ]
  }
}

Sprawdzenie podpisu

Nagłówek X-Podpiszmy-Signature to HMAC-SHA256 policzony z surowego ciała zapytania i Twojego sekretu, w postaci sha256=HEX. Licz go przed sparsowaniem JSON-a. Przepuszczenie treści przez dekoder i koder zmienia bajty, a wtedy podpis przestaje pasować, mimo że wszystko jest w porządku. Porównuj podpisy funkcją odporną na pomiar czasu.

// PHP
$body = file_get_contents('php://input');
$oczekiwany = 'sha256=' . hash_hmac('sha256', $body, $sekret);

if (! hash_equals($oczekiwany, $_SERVER['HTTP_X_PODPISZMY_SIGNATURE'] ?? '')) {
    http_response_code(400);
    exit;
}

$zdarzenie = json_decode($body, true);
# Python
import hmac, hashlib

oczekiwany = "sha256=" + hmac.new(sekret.encode(), body, hashlib.sha256).hexdigest()

if not hmac.compare_digest(oczekiwany, naglowki.get("X-Podpiszmy-Signature", "")):
    return 400

Ponowienia

Odpowiedz kodem 2xx, gdy przyjmiesz zdarzenie. Każda inna odpowiedź i każdy brak połączenia uruchamiają ponowienie: łącznie pięć podejść z odstępami 10 sekund, minuta, 5 minut i 30 minut. Nagłówek X-Podpiszmy-Delivery jest ten sam we wszystkich podejściach tego samego zdarzenia, więc użyj go do odsiania duplikatów. Wszystkie próby razem z kodami odpowiedzi widzisz w panelu oraz pod GET /webhooks/{id}/dostarczenia.

Limity zapytań

Limity liczymy na konto, nie na adres IP, więc integracja na współdzielonym hostingu nie konkuruje z cudzym ruchem. Odczyt: 120 zapytań na minutę. Zapis, czyli tworzenie dokumentów, wysyłka, anulowanie, przypomnienia i zmiany webhooków: 20 na minutę. Po przekroczeniu dostajesz kod 429 razem z nagłówkiem Retry-After. Bieżące zużycie widzisz w panelu.

Kody odpowiedzi

Kod Znaczenie
200, 201 Zapytanie wykonane.
401 Brak tokena, token odwołany albo nieprawidłowy.
402 Konto nie ma planu z dostępem do API. Odpowiedź podaje nazwę planu i adres cennika.
404 Nie ma takiego dokumentu na tym koncie. Tego samego kodu używamy dla cudzych dokumentów, żeby nie potwierdzać, że istnieją.
409 Dokument jest w stanie, w którym ta operacja nie ma sensu. Na przykład podpisany plik przed kompletem podpisów.
422 Dane zapytania są nieprawidłowe albo wyczerpała się pula dokumentów.
429 Przekroczony limit zapytań tego konta albo przypomnienie wysłane zbyt szybko po poprzednim.

Podłącz swój system do podpisu

API i webhooki działają w planie Business. Umowy wychodzą z Twojego systemu, a podpisany plik wraca do niego automatycznie.