Public Jobs API
Bauen Sie Ihre eigene Jobseite (z. B. mit Next.js auf Vercel) auf Ihrer Kit-Hiring-Pipeline auf. Listen Sie veröffentlichte Stellen auf und empfangen Sie Bewerbungen über eine öffentliche REST-API, ein offizielles TypeScript-SDK und eine Next.js-Vorlage mit einem Klick.
Warum das zählt
Das gehostete Karriereportal und das einbettbare Widget von Kit decken die meisten Anforderungen ab. Wenn Sie aber die volle Kontrolle über das Design möchten – eine maßgeschneiderte Karriereseite, eine eigene Landingpage pro Stelle oder eine Jobbörse, die zu Ihrem Produkt passt —, können Sie mit der Public Jobs API Ihre veröffentlichten Stellen auslesen und Bewerbungen direkt in Ihre Kit-Pipeline einreichen, während Kit weiterhin Screening, Phasen, Interviews und die Kommunikation mit Kandidaten übernimmt.
Es gibt außerdem ein offizielles TypeScript-SDK und eine Next.js-Vorlage mit Ein-Klick-Deploy, mit denen Sie in wenigen Minuten eine eigene Jobseite ausliefern – oder Sie überspringen beides und rufen die untenstehenden REST-Endpunkte direkt mit einem beliebigen HTTP-Client auf.
API-Schlüssel
Erstellen Sie ein Schlüsselpaar unter Hiring → Karriereportal → Öffentliche API-Schlüssel. Jedes Paar besteht aus:
-
Veröffentlichbarer Schlüssel (
pk_…) – kann bedenkenlos im Browser ausgeliefert werden. Er kann veröffentlichte Stellen lesen und Bewerbungen einreichen, sonst nichts, und gibt niemals Kandidatendaten preis. Bevor er Bewerbungen einreichen oder presigned Uploads anfordern kann, müssen Sie mindestens eine Bot-Abwehr einrichten – eine Origin-Allowlist oder Ihr eigenes Cloudflare-Turnstile-Widget —, andernfalls werden diese Anfragen mit403 bot_protection_requiredabgelehnt. Das Lesen von Stellen funktioniert auch ohne diese Konfiguration. -
Geheimer Schlüssel (
sk_…) – ausschließlich für die serverseitige Nutzung (z. B. eine Next.js Server Action). Er überspringt die browserseitigen Origin-/Turnstile-Prüfungen. Geben Sie ihn niemals in clientseitigem Code preis. Keiner der beiden Schlüssel kann personenbezogene Kandidatendaten lesen.
Der geheime Schlüssel wird nur ein einziges Mal angezeigt – bei der Erstellung oder Rotation. Sie können ihn jederzeit über die Einstellungsseite des Schlüssels rotieren; der bisherige geheime Schlüssel funktioniert sofort nicht mehr.
Authentifizieren Sie jede Anfrage mit einem Bearer-Header:
Authorization: Bearer sk_your_secret_key
Endpunkte
Basis-URL: https://startupkit.app (oder Ihre benutzerdefinierte Karriere-Domain).
Veröffentlichte Stellen auflisten
GET /api/public/v1/jobs?department=&location=&employment_type=&remote=&page=&per_page=
Gibt ausschließlich veröffentlichte Stellen Ihres Kontos zurück.
{
"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 }
}
Die id ist das öffentliche Token der Stelle – verwenden Sie es für die Detail- und Bewerbungs-Endpunkte.
Eine Stelle samt Bewerbungsformular abrufen
GET /api/public/v1/jobs/:public_token
Gibt die Stelle zurück, ergänzt um ein application_form, das genau beschreibt, welche Felder und Fragen zu rendern sind, welcher Einwilligungshinweis anzuzeigen ist, ob ein Lebenslauf erforderlich ist sowie welche Dateitypen und -größen dafür akzeptiert werden und ob Turnstile erforderlich ist.
{
"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": {
"required": false,
"content_types": ["application/pdf", "application/msword", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"],
"max_byte_size": 10485760
},
"turnstile": { "required": false, "sitekey": null }
}
}
required-Flags werden serverseitig durchgesetzt. Steht resume.required auf true, wird eine Bewerbung ohne resume_signed_id mit 422 validation_failed abgelehnt – dasselbe gilt für jedes unbeantwortete Feld und jede unbeantwortete Frage mit required: true. Wenn Sie das Formular dynamisch aus diesem Schema rendern, berücksichtigen Sie diese Flags – sonst füllen Kandidaten alles aus und werden erst beim Absenden abgewiesen.
Lebenslauf hochladen (presigned)
Lebensläufe werden direkt in den Speicher hochgeladen und passieren daher niemals Ihren Server (das vermeidet Body-Größenlimits in Serverless-Umgebungen).
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": "…" } }
}
Senden Sie die Dateibytes per PUT an direct_upload.url mit den zurückgegebenen headers, und übergeben Sie anschließend die signed_id als resume_signed_id, wenn Sie die Bewerbung einreichen.
Bewerbung einreichen
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>"
}
Gibt 201 mit einer minimalen Bestätigung ohne personenbezogene Daten zurück:
{ "id": "app_9fQ…", "status": "submitted", "job": "JdK2hQ8…", "submitted_at": "2026-06-11T09:30:00Z" }
Das turnstile_token wird nur bei Browser-Einreichungen (pk_) benötigt, wenn für den Schlüssel Turnstile konfiguriert ist; serverseitige Aufrufe (sk_) überspringen es.
Talentpool
Nicht jeder, dem Ihr Unternehmen gefällt, passt heute auf eine offene Stelle. Der Talentpool erfasst genau diese Menschen – E-Mail-Adresse, LinkedIn, optional einen Lebenslauf –, sodass Sie sie einladen können, sobald die passende Stelle frei wird. Einträge landen zunächst unverifiziert im System: Kit verschickt einen Bestätigungslink, und niemand gelangt über diesen Endpunkt in Ihren Pool, bevor er ihn angeklickt hat. (Admins können Lebensläufe außerdem direkt importieren – das ist ein separater Zugang mit eigenen Regeln.)
Bei einer Talentpool-Anmeldung bittet jemand darum, seine Daten ohne konkrete Stelle bei Ihnen zu hinterlegen – dafür braucht es eine ausdrückliche Einwilligung: ein tatsächlich aktiviertes Kontrollkästchen, nicht bloß einen Hinweis, den Sie anzeigen. Eine öffentliche Anmeldung ohne diese Einwilligung lehnt Kit ab. Bei einem Admin-Import wird stattdessen eine Bestätigung der Rechtsgrundlage festgehalten – denn dort ist niemand da, der etwas ankreuzen könnte.
Das Anmeldeformular abrufen
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 }
}
Rendern Sie consent.disclosure_html als Beschriftung eines Kontrollkästchens, das nicht aktiviert vorbelegt ist. Es ist Ihr eigener Einwilligungstext – bearbeiten Sie ihn unter Hiring → Einstellungen → Einwilligung, zusammen mit retention_months: Dieser Wert steuert, wann Kit Einträge anonymisiert, deren Einwilligung niemand erneuert hat.
Dem Talentpool beitreten
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>"
}
Gibt 201 mit einer Bestätigung ohne personenbezogene Daten zurück:
{ "id": "tpe_9fQ…", "status": "pending_verification", "submitted_at": "2026-06-11T09:30:00Z" }
Lebensläufe nutzen denselben presigned Upload wie Bewerbungen – fordern Sie eine signed_id an, senden Sie die Bytes per PUT und übergeben Sie sie anschließend als resume_signed_id.
Festhalten, wer eingewilligt hat
Zu jedem Eintrag speichert Kit einen Einwilligungsnachweis: den genau angezeigten Text, den Zeitstempel und die IP-Adresse der Person, die zugestimmt hat. Am letzten Feld scheitern serverseitige Integrationen gern unbemerkt.
Wird aus dem Browser mit einem pk_-Schlüssel eingereicht, stellt Kit die IP-Adresse selbst fest und ignoriert ein mitgesendetes consent_ip_address – ein Schlüssel, der im JavaScript der Seite ausgeliefert wird, darf seinen eigenen Nachweis nicht schreiben können.
Wird von Ihrem Server mit einem sk_-Schlüssel eingereicht, sieht Kit die IP-Adresse Ihres Servers, nicht die der Person. Senden Sie die tatsächliche Adresse als consent_ip_address – in einer Vercel Server Action ist das der erste Eintrag in x-forwarded-for:
import { headers } from "next/headers";
const forwarded = (await headers()).get("x-forwarded-for");
const consentIp = forwarded?.split(",")[0]?.trim();
Dieser Wert ist von Ihnen behauptet, nicht bewiesen: Kit speichert, was Sie senden, sobald es sich als IP-Adresse parsen lässt, und lehnt alles andere mit 422 invalid_consent_ip ab. Lassen Sie das Feld weg, wenn Sie die Adresse wirklich nicht kennen – Kit hält dann keine IP fest, und das ist eine ehrliche Lücke statt eines Nachweises, der auf die falsche Maschine zeigt.
Fehler
Fehler werden in einem einheitlichen Format zurückgegeben:
{ "error": { "code": "validation_failed", "message": "Email can't be blank", "fields": { "email": ["can't be blank"] } } }
| Status | Code | Bedeutung |
|---|---|---|
| 401 | invalid_key |
Fehlender oder ungültiger API-Schlüssel |
| 403 | bot_protection_required |
Publishable Key (pk_) hat weder eine Origin-Allowlist noch Turnstile konfiguriert |
| 403 | origin_not_allowed |
Browser-Origin steht nicht auf der Allowlist des Schlüssels |
| 404 | not_found |
Stelle nicht gefunden oder nicht veröffentlicht |
| 409 | already_applied |
Diese E-Mail-Adresse hat sich bereits auf diese Stelle beworben |
| 409 | already_in_talent_pool |
Diese E-Mail-Adresse ist bereits im Talentpool |
| 422 | validation_failed |
Ungültige Bewerbungsfelder – dazu zählen ein fehlender erforderlicher Lebenslauf sowie unbeantwortete Pflichtfelder und -fragen (siehe fields) |
| 422 | consent_required |
Talentpool-Anmeldung ohne aktiviertes Einwilligungs-Kontrollkästchen eingegangen |
| 422 | invalid_consent_ip |
consent_ip_address ist keine gültige IP-Adresse |
| 422 | turnstile_failed |
Turnstile-Verifizierung fehlgeschlagen |
| 422 |
invalid_content_type / file_too_large / invalid_byte_size
|
Abgelehnter Lebenslauf-Upload |
Status-Updates über Webhooks
Um Bewerbungen nach der Einreichung weiterzuverfolgen, konfigurieren Sie ausgehende Webhooks. Relevante Ereignisse sind unter anderem application.submitted, application.advanced und application.rejected sowie job_posting.published/paused/closed. Bewerbungs-Payloads enthalten sowohl die numerische id als auch die API-prefix_id (app_…) sowie das public_token der Stelle, sodass Sie Webhook-Ereignisse mit API-Datensätzen verknüpfen können.
SDK & Next.js-Vorlage
Zwei offizielle, quelloffene Ausgangspunkte setzen auf der oben beschriebenen REST-Schnittstelle auf – nutzen Sie einen davon oder überspringen Sie beide und rufen die Endpunkte direkt mit einem beliebigen HTTP-Client auf.
-
TypeScript-SDK –
@startupkit-app/jobs. Ein typisierter, abhängigkeitsfreier Client (nativesfetch, ESM + CJS, Node ≥ 18.17), der in Node, Browsern und Edge-Runtimes läuft. Installieren Sie ihn mitnpm install @startupkit-app/jobsund dann: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 });Übergeben Sie in Browser-Code
publishableKey(pk_…) stattsecretKey. Weitere Methoden:allJobs()iteriert asynchron über alle Seiten,createUpload()bietet feinere Kontrolle über den presigned Upload, undgetTalentPool()/joinTalentPool()decken die oben beschriebene Talentpool-Anmeldung ab. Nicht-2xx-Antworten werfenKitApiError(mit.codeund.fields); Fehler, die die API nie erreichen, werfenKitNetworkError. Der Client verwendet standardmäßig die Basis-URLhttps://app.startupkit.app; übergeben SiebaseUrl, um sie für eine benutzerdefinierte Karriere-Domain zu überschreiben. -
Next.js-Vorlage –
nextjs-job-board. Eine produktionsreife Karriereseite (Next.js App Router, Server Components + Server Actions, ISR mit tag-basierter Revalidierung), die Sie forken oder mit einem Klick deployen können. Sie rendert das Bewerbungsformular dynamisch aus dem API-Schema, führt presigned Lebenslauf-Uploads direkt in den Speicher durch, gibt schema.org-JobPosting-JSON-LD aus (bereit für Google for Jobs) und revalidiert optional sofort per Webhook. Setzen Sie eine einzige Umgebungsvariable –STARTUPKIT_SECRET_KEY(Ihrsk_…-Schlüssel) – und deployen Sie:Live-Demo: nextjs-job-board-orcin.vercel.app. Optionale Umgebungsvariablen:
STARTUPKIT_BASE_URL(Standard:https://app.startupkit.app),NEXT_PUBLIC_TURNSTILE_SITE_KEY,REVALIDATE_SECRETundNEXT_PUBLIC_COMPANY_NAME.
Beide nutzen dieselbe Schnittstelle, sodass Sie auch mit jedem beliebigen Framework über einfaches HTTP entwickeln können. Eine typische eigene Jobseite verbindet vier Aufrufe: Stellen auflisten, eine Stelle samt Formular abrufen, einen presigned Upload für den Lebenslauf anfordern und die Bewerbung einreichen. Da alles reines JSON über HTTPS ist, funktioniert dieselbe Integration serverseitig (sk_-Schlüssel) wie im Browser (pk_-Schlüssel mit konfigurierter Bot-Abwehr).
Rate Limits
Bewerbungseinreichungen sind auf 10/Stunde pro IP begrenzt, Upload-Anfragen auf 30/Stunde pro IP und 300/Stunde pro Schlüssel – zusätzlich zu den globalen API-Rate-Limits. Browser-Schlüssel sind außerdem durch ihre Origin-Allowlist und optional durch Turnstile geschützt.
Talentpool-Anmeldungen sind auf 100/Stunde pro Schlüssel begrenzt. Browser-Anmeldungen (pk_) unterliegen zusätzlich einer Grenze von 5/Stunde pro IP, serverseitige Anmeldungen (sk_) dagegen nicht: Sie alle erreichen Kit von derselben Ausgangsadresse, und eine Obergrenze pro IP würde Ihre gesamte Website ausbremsen statt einer einzelnen Person.