Logo StartupKit
PL

Publiczne API ofert pracy

Zbuduj własną stronę z ofertami pracy (np. w Next.js na Vercel) na bazie pipeline'u rekrutacyjnego Kit. Pobieraj opublikowane ogłoszenia i przyjmuj aplikacje przez publiczne REST API, oficjalne SDK w TypeScript oraz szablon Next.js wdrażany jednym kliknięciem.

Własna strona z ofertami pracy

Hostowany portal kariery i widget do osadzania pozwalają wyświetlać ogłoszenia bez budowania własnej strony. Jeśli chcesz samodzielnie zaprojektować jej wygląd (własną stronę kariery, osobny landing page dla każdego stanowiska albo portal z ofertami dopasowany do twojego produktu), Publiczne API ofert pracy pozwala odczytywać opublikowane ogłoszenia i przesyłać aplikacje prosto do pipeline’u w Kit, podczas gdy Kit nadal odpowiada za screening, etapy, rozmowy i komunikację z kandydatami.

Dostępne są też oficjalne SDK w TypeScript oraz szablon Next.js wdrażany jednym kliknięciem. Możesz też wywoływać opisane niżej endpointy REST bezpośrednio, dowolnym klientem HTTP.

Zobacz, jak korzysta z tego nasz klient, na przykładzie własnej strony kariery Fourthwall. Na tej samej stronie opisujemy też projekt startowy Next.js od Kit.

Klucze API

Utwórz parę kluczy w Hiring → Career Portal → Public API Keys (Publiczne klucze API). Parę tworzą:

  • Klucz publikowalny (pk_…): bezpieczny do użycia w przeglądarce. Pozwala odczytywać opublikowane ogłoszenia i przesyłać aplikacje. Nie ujawnia danych kandydatów. Zanim będzie mógł przesyłać aplikacje albo prosić o presigned upload, musisz skonfigurować przynajmniej jedno zabezpieczenie przed botami (listę dozwolonych originów albo własny widget Cloudflare Turnstile), w przeciwnym razie takie żądania są odrzucane z 403 bot_protection_required. Odczyt ogłoszeń działa bez tej konfiguracji.
  • Tajny klucz (sk_…): wyłącznie do użycia po stronie serwera (np. w Server Action w Next.js). Pomija przeglądarkowe sprawdzanie originu i Turnstile. Nigdy nie umieszczaj go w kodzie po stronie klienta. Żaden z kluczy nie ma dostępu do danych osobowych kandydatów.

Tajny klucz jest pokazywany tylko raz, przy utworzeniu lub rotacji. Możesz go wymienić w dowolnym momencie ze strony ustawień klucza; poprzedni klucz natychmiast przestaje działać.

Każde żądanie uwierzytelniaj nagłówkiem bearer:

Authorization: Bearer sk_your_secret_key

Endpointy

Bazowy URL: https://startupkit.app (albo twoja niestandardowa domena kariery).

Pobieranie listy opublikowanych ogłoszeń

GET /api/public/v1/jobs?department=&location=&employment_type=&remote=&page=&per_page=

Zwraca wyłącznie opublikowane ogłoszenia z twojego konta.

{
  "data": [
    {
      "id": "JdK2hQ8…",
      "title": "Senior Rails Developer",
      "department": "Engineering",
      "location": "Remote",
      "employment_type": "full_time",
      "remote": true,
      "published_at": "2026-06-01T12:00:00Z",
      "url": "https://careers.yourco.com/JdK2hQ8…",
      "salary": { "min": 120000, "max": 160000, "currency": "USD", "period": "YEAR" }
    }
  ],
  "pagination": { "current_page": 1, "total_pages": 3, "total_count": 42, "per_page": 20 }
}

id to publiczny token ogłoszenia; użyj go w endpointach szczegółów i aplikowania. Wartość employment_type to full_time, part_time, b2b, contract lub internship. Tę samą wartość możesz przekazać przy pobieraniu listy, żeby filtrować opublikowane ogłoszenia.

Pobieranie ogłoszenia z formularzem aplikacyjnym

GET /api/public/v1/jobs/:public_token

Zwraca ogłoszenie wraz z application_form, który dokładnie opisuje, jakie pola i pytania wyrenderować, jaką klauzulę informacyjną pokazać, czy CV jest wymagane oraz jakie formaty i rozmiary plików są akceptowane, a także czy wymagany jest Turnstile.

{
  "id": "JdK2hQ8…",
  "title": "Senior Rails Developer",
  "description_html": "<p>We're hiring…</p>",
  "accepting_applications": true,
  "stages": [
    { "name": "Application Review", "type": "application_form" },
    {
      "name": "Work sample",
      "type": "code_assignment",
      "compensation": { "amount": 250, "currency": "USD" }
    }
  ],
  "application_form": {
    "fields": [
      { "name": "cover_letter", "type": "textarea", "label": "Cover letter", "required": false }
    ],
    "questions": [
      { "key": "why_us", "type": "text", "prompt": "Why do you want to join?", "required": true, "max_length": 2000 }
    ],
    "consent_disclosure_html": "<p>By applying you agree…</p>",
    "resume": {
      "required": false,
      "content_types": ["application/pdf", "application/msword", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"],
      "max_byte_size": 10485760
    },
    "turnstile": { "required": false, "sitekey": null }
  }
}

Jeśli etap przewiduje jednorazowe wynagrodzenie, zawiera obiekt compensation z kwotą w pełnych jednostkach waluty i jej kodem. Dla bezpłatnych etapów pole jest pomijane. Chodzi o wynagrodzenie za wykonanie zadania na danym etapie, nie o pensję na stanowisku. Obiekt nie ujawnia metody ani statusu płatności kandydata. Endpoint listy nie zwraca etapów; pobierz szczegóły ogłoszenia, żeby odczytać proces i wynagrodzenie za zadania.

Flagi required są egzekwowane po stronie serwera. Gdy resume.required ma wartość true, aplikacja bez resume_signed_id zostaje odrzucona z 422 validation_failed, tak samo jak przy każdym pominiętym polu lub pytaniu oznaczonym required: true. Jeśli renderujesz formularz dynamicznie na podstawie tego schematu, uwzględnij te flagi, żeby kandydat nie zobaczył odmowy dopiero po wypełnieniu całego formularza.

Przesyłanie CV (presigned upload)

CV trafiają bezpośrednio do magazynu plików, więc nigdy nie przechodzą przez twój serwer (co omija limity rozmiaru treści żądania w środowiskach serverless).

POST /api/public/v1/direct_uploads
{ "blob": { "filename": "cv.pdf", "byte_size": 102400, "checksum": "<base64 MD5>", "content_type": "application/pdf" } }
{
  "signed_id": "eyJf…",
  "direct_upload": { "url": "https://…s3…", "headers": { "Content-Type": "application/pdf", "Content-MD5": "" } }
}

Wyślij bajty pliku metodą PUT na direct_upload.url ze zwróconymi headers, a następnie przekaż signed_id jako resume_signed_id przy wysyłaniu aplikacji.

Wysyłanie aplikacji

POST /api/public/v1/jobs/:public_token/applications
{
  "application": {
    "email": "[email protected]",
    "first_name": "Ada",
    "last_name": "Lovelace",
    "phone": "+1 555 0100",
    "responses": { "cover_letter": "…", "why_us": "…" },
    "resume_signed_id": "eyJf…"
  },
  "turnstile_token": "<token>"
}

Zwraca 201 z minimalnym potwierdzeniem bez danych osobowych:

{ "id": "app_9fQ…", "status": "submitted", "job": "JdK2hQ8…", "submitted_at": "2026-06-11T09:30:00Z" }

turnstile_token jest potrzebny tylko przy wysyłce z przeglądarki (pk_), gdy klucz ma skonfigurowany Turnstile; wywołania po stronie serwera (sk_) go pomijają.

Pula talentów

Nie każdy, komu podoba się twoja firma, pasuje dziś do któregoś z otwartych stanowisk. Pula talentów zbiera właśnie takie osoby (adres e-mail, LinkedIn, opcjonalnie CV), żeby można było odezwać się do nich, gdy otworzy się odpowiednia rekrutacja. Wpisy trafiają tam jako niezweryfikowane: Kit wysyła mailem link potwierdzający i nikt nie znajdzie się w puli przez ten endpoint, dopóki w niego nie kliknie. (Administratorzy mogą też importować CV bezpośrednio na osobnych zasadach.)

Osoba zapisująca się do puli talentów prosi o przechowywanie danych na potrzeby przyszłych rekrutacji, bez aplikowania na konkretne stanowisko. Taki zapis wymaga więc wyraźnej zgody: faktycznie zaznaczonego pola wyboru, a nie samej klauzuli pokazanej na stronie. Publiczny zapis bez niej zostaje odrzucony. Import wykonany przez administratora zapisuje zamiast tego oświadczenie o podstawie prawnej, ponieważ kandydat nie wypełnia wtedy formularza zgody.

Pobieranie formularza zapisu

GET /api/public/v1/talent_pool
{
  "accepting_signups": true,
  "consent": {
    "required": true,
    "disclosure_html": "<p>Keep my details on file for 24 months…</p>",
    "retention_months": 24,
    "privacy_policy_url": "https://yourco.com/privacy"
  },
  "fields": [
    { "name": "email", "required": true },
    { "name": "linkedin_url", "required": false },
    { "name": "resume_signed_id", "required": false }
  ],
  "resume": {
    "required": false,
    "content_types": ["application/pdf", "application/msword", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"],
    "max_byte_size": 10485760
  },
  "turnstile": { "required": false, "sitekey": null }
}

Wyrenderuj consent.disclosure_html jako etykietę pola wyboru, które domyślnie jest niezaznaczone. To twoja własna treść zgody, edytowana w Hiring → Settings → Consent razem z retention_months, które decyduje o tym, kiedy Kit anonimizuje wpisy, dla których nie odnowiono zgody.

Zapis do puli talentów

POST /api/public/v1/talent_pool/entries
{
  "talent_pool_entry": {
    "email": "[email protected]",
    "linkedin_url": "https://linkedin.com/in/ada",
    "resume_signed_id": "eyJf…",
    "consent": true,
    "consent_ip_address": "203.0.113.7"
  },
  "turnstile_token": "<token>"
}

Zwraca 201 z potwierdzeniem bez danych osobowych:

{ "id": "tpe_9fQ…", "status": "pending_verification", "submitted_at": "2026-06-11T09:30:00Z" }

CV przesyła się dokładnie tak samo jak przy aplikacjach: poproś o signed_id, wyślij bajty metodą PUT, a potem przekaż go jako resume_signed_id.

Rejestrowanie tego, kto wyraził zgodę

Przy każdym wpisie Kit przechowuje poświadczenie zgody: dokładną treść, którą pokazano, znacznik czasu oraz adres IP osoby, która zgodę wyraziła. Przy zapisie adresu IP łatwo o błąd w integracji po stronie serwera.

Przy wysyłce z przeglądarki kluczem pk_ Kit sam odczytuje adres IP i ignoruje consent_ip_address, które przyślesz. Klucz dostępny w kodzie JavaScript na stronie nie może posłużyć do samodzielnego wskazania adresu IP w poświadczeniu.

Przy wysyłce z twojego serwera kluczem sk_ Kit widzi adres twojego serwera, a nie osoby, która się zapisuje. Prawdziwy adres prześlij w consent_ip_address; w Server Action na Vercelu to pierwszy adres z nagłówka x-forwarded-for:

import { headers } from "next/headers";

const forwarded = (await headers()).get("x-forwarded-for");
const consentIp = forwarded?.split(",")[0]?.trim();

Za prawdziwość tej wartości odpowiadasz ty; Kit jej nie weryfikuje: zapisuje to, co przyślesz, o ile da się to zinterpretować jako adres IP, a wszystko inne odrzuca z 422 invalid_consent_ip. Jeśli naprawdę go nie znasz, pomiń to pole. Kit nie zapisze wtedy adresu IP w poświadczeniu.

Błędy

Błędy mają wspólny format odpowiedzi:

{ "error": { "code": "validation_failed", "message": "Email can't be blank", "fields": { "email": ["can't be blank"] } } }
Status Kod Znaczenie
401 invalid_key Brakujący lub nieprawidłowy klucz API
403 bot_protection_required Klucz publikowalny (pk_) nie ma skonfigurowanej listy dozwolonych originów ani Turnstile
403 origin_not_allowed Origin przeglądarki spoza listy dozwolonych dla klucza
404 not_found Ogłoszenie nie istnieje lub nie jest opublikowane
409 already_applied Z tego adresu e-mail już zaaplikowano na to ogłoszenie
409 already_in_talent_pool Ten adres e-mail jest już w puli talentów
422 validation_failed Nieprawidłowe pola aplikacji, w tym brak wymaganego CV albo pominięte wymagane pole lub pytanie (zobacz fields)
422 consent_required Zapis do puli talentów przyszedł bez zaznaczonego pola zgody
422 invalid_consent_ip consent_ip_address nie jest prawidłowym adresem IP
422 turnstile_failed Weryfikacja Turnstile nie powiodła się
422 invalid_content_type / file_too_large / invalid_byte_size Odrzucony upload CV

Aktualizacje statusu przez webhooki

Żeby śledzić aplikacje po ich wysłaniu, skonfiguruj webhooki wychodzące. Istotne zdarzenia to m.in. application.submitted, application.advanced i application.rejected, a także job_posting.published/paused/closed. Payloady aplikacji zawierają zarówno numeryczne id, jak i prefix_id z API (app_…) oraz public_token ogłoszenia, więc zdarzenia z webhooków łatwo powiążesz z rekordami z API.

SDK i szablon Next.js

Oficjalne SDK i szablon mają otwarty kod i korzystają z opisanego wyżej kontraktu REST. Możesz użyć jednego z nich lub wywoływać endpointy bezpośrednio dowolnym klientem HTTP.

  • SDK w TypeScript: @startupkit-app/jobs. Typowany klient bez zależności (natywny fetch, ESM + CJS, Node ≥ 18.17), działający w Node, przeglądarkach i runtime’ach edge. Zainstaluj go poleceniem npm install @startupkit-app/jobs, a następnie:

    import { createClient } from "@startupkit-app/jobs";
    
    const kit = createClient({ secretKey: process.env.KIT_SECRET_KEY });
    
    const page = await kit.listJobs({ department: "Engineering", remote: true });
    const job = await kit.getJob(page.data[0].id);
    const { signed_id } = await kit.uploadFile(resumeFile);
    await kit.apply(job.id, { email: "[email protected]", resume_signed_id: signed_id });
    

    W kodzie przeglądarkowym przekaż publishableKey (pk_…) zamiast secretKey. Pozostałe metody: allJobs() iteruje asynchronicznie po wszystkich stronach, createUpload() daje kontrolę nad presigned uploadem na niższym poziomie, a getTalentPool() / joinTalentPool() obsługują opisany wyżej zapis do puli talentów. Odpowiedzi inne niż 2xx rzucają KitApiError (z .code i .fields); błędy, które nigdy nie docierają do API, rzucają KitNetworkError. Klient domyślnie używa bazowego URL https://app.startupkit.app; przekaż baseUrl, aby nadpisać go własną domeną kariery.

  • Szablon Next.js: nextjs-job-board. Gotowa do produkcji strona kariery (Next.js App Router, Server Components + Server Actions, ISR z rewalidacją opartą na tagach), którą możesz sforkować albo wdrożyć jednym kliknięciem. Renderuje formularz aplikacyjny dynamicznie ze schematu API, wykonuje presigned uploady CV bezpośrednio do magazynu, emituje JSON-LD schema.org JobPosting (gotowe pod Google for Jobs) i opcjonalnie rewaliduje natychmiast przez webhooki. Ustaw jedną zmienną środowiskową STARTUPKIT_SECRET_KEY (twój klucz sk_…) i wdróż:

    Deploy with Vercel

    Demo na żywo: nextjs-job-board-orcin.vercel.app. Opcjonalne zmienne środowiskowe: STARTUPKIT_BASE_URL (domyślnie https://app.startupkit.app), NEXT_PUBLIC_TURNSTILE_SITE_KEY, REVALIDATE_SECRET oraz NEXT_PUBLIC_COMPANY_NAME.

Oba korzystają z opisanego wyżej kontraktu, więc możesz też budować w dowolnym frameworku przez zwykłe HTTP. Typowa własna strona z ofertami łączy cztery wywołania: pobranie listy ogłoszeń, pobranie ogłoszenia z formularzem, poproszenie o presigned upload dla CV oraz wysłanie aplikacji. Ponieważ wszystko to zwykły JSON po HTTPS, ta sama integracja działa po stronie serwera (klucz sk_) i w przeglądarce (klucz pk_ ze skonfigurowanym zabezpieczeniem przed botami).

Limity zapytań

Wysyłanie aplikacji jest ograniczone do 10/godz. na IP, a żądania uploadu do 30/godz. na IP i 300/godz. na klucz, oprócz globalnych limitów API. Klucze przeglądarkowe są dodatkowo chronione listą dozwolonych originów i opcjonalnie przez Turnstile.

Zapisy do puli talentów są ograniczone do 100/godz. na klucz. Zapisy z przeglądarki (pk_) mają dodatkowo limit 5/godz. na IP, a serwerowe (sk_) nie mają go wcale: wszystkie docierają do Kit z tego samego adresu wyjściowego, więc limit na IP ograniczałby zgłoszenia z całej strony, a nie od jednej osoby.

Wpisz, aby wyszukać...