## Dlaczego to ważne

Hostowany [portal kariery](/docs/setting-up-your-career-portal) i [widget do osadzania](/docs/embedding-your-career-portal) pokrywają większość potrzeb. Jeśli jednak chcesz mieć pełną kontrolę nad designem — własną stronę kariery, osobny landing page dla każdego stanowiska albo portal z ofertami dopasowany do Twojego produktu — **Public Jobs API** 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.

Jest też oficjalny **szablon Next.js** do wdrożenia jednym kliknięciem oraz **SDK w TypeScript**, dzięki którym własną stronę z ofertami wystawisz w kilka minut.

## Klucze API

Utwórz parę kluczy w **Hiring → Career Portal → Public API Keys**. Każda para składa się z:

- **Publishable key (`pk_…`)** — bezpieczny do użycia w przeglądarce. Może odczytywać opublikowane ogłoszenia i przesyłać aplikacje, nic więcej. Nigdy nie ujawnia danych kandydatów. Możesz ograniczyć go do konkretnych originów i zabezpieczyć własnym widgetem Cloudflare Turnstile.
- **Secret key (`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.

Secret key jest pokazywany tylko raz — przy utworzeniu lub rotacji. Możesz go zrotować w dowolnym momencie ze strony ustawień klucza; poprzedni secret natychmiast przestaje działać.

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

```
Authorization: Bearer sk_your_secret_key
```

## Endpointy

Bazowy URL: `https://app.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.

```json
{
  "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.

### 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ę zgody pokazać, jakie typy i rozmiary plików CV są akceptowane oraz czy wymagany jest Turnstile.

```json
{
  "id": "JdK2hQ8…",
  "title": "Senior Rails Developer",
  "description_html": "<p>We're hiring…</p>",
  "accepting_applications": true,
  "stages": [{ "name": "Application Review", "type": "application_form" }],
  "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": {
      "content_types": ["application/pdf", "application/msword", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"],
      "max_byte_size": 10485760
    },
    "turnstile": { "required": false, "sitekey": null }
  }
}
```

### Przesyłanie CV (presigned upload)

CV trafiają bezpośrednio do storage'u, więc nigdy nie przechodzą przez Twój serwer (co omija limity rozmiaru body w środowiskach serverless).

```
POST /api/public/v1/direct_uploads
{ "blob": { "filename": "cv.pdf", "byte_size": 102400, "checksum": "<base64 MD5>", "content_type": "application/pdf" } }
```

```json
{
  "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": "candidate@example.com",
    "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:

```json
{ "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ą.

## Błędy

Błędy zwracane są w spójnej kopercie:

```json
{ "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 | `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` | Ten e-mail już zaaplikował na to ogłoszenie |
| 422 | `validation_failed` | Nieprawidłowe pola aplikacji (zobacz `fields`) |
| 422 | `turnstile_failed` | Weryfikacja Turnstile nie powiodła się |
| 422 | `invalid_content_type` / `file_too_large` | Odrzucony upload CV |

## Aktualizacje statusu przez webhooki

Żeby śledzić aplikacje po ich wysłaniu, skonfiguruj [webhooki wychodzące](/docs/webhooks). 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 API-owe `prefix_id` (`app_…`) oraz `public_token` ogłoszenia, więc zdarzenia z webhooków łatwo powiążesz z rekordami z API.

## SDK i szablon Next.js

- **SDK w TypeScript** — `npm install @startupkit-app/jobs`. Typowany klient bez zależności z metodami `listJobs`, `getJob`, `uploadFile` i `apply`.
- **Szablon Next.js** — portal z ofertami do wdrożenia jednym kliknięciem ([Deploy to Vercel](https://vercel.com)), który możesz sforkować i dostosować. Ma od razu skonfigurowany secret key, dynamiczne formularze aplikacyjne, presigned upload CV oraz SEO/JSON-LD.

Oba korzystają z opisanego wyżej kontraktu, więc równie dobrze możesz budować w dowolnym frameworku, używając czystego HTTP.

## Rate limiting

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