### Install @startupkit/jobs Source: https://github.com/startupkit-app/jobs-js/blob/main/README.md Install the package using npm. ```sh npm install @startupkit/jobs ``` -------------------------------- ### Browser Quickstart with Publishable Key and Turnstile Source: https://github.com/startupkit-app/jobs-js/blob/main/README.md Use a publishable key for browser-side operations. Integrates with Cloudflare Turnstile for application security when required. ```ts import { createClient } from "@startupkit/jobs"; const kit = createClient({ publishableKey: "pk_live_…" }); const job = await kit.getJob("tok_abc123"); // Render job.application_form.fields and job.application_form.questions, // show job.application_form.consent_disclosure_html near the submit button. const result = await kit.apply( job.id, { email: "jane@example.com", first_name: "Jane", last_name: "Doe", responses: { motivation: "…" }, // keyed by Question.key }, { turnstileToken } // from the Turnstile widget callback ); console.log(result.id, result.status); // "app_…", "submitted" ``` -------------------------------- ### Get Job Details Source: https://github.com/startupkit-app/jobs-js/blob/main/README.md Fetches the full details for a specific job, including its description, application stages, and form definition. ```APIDOC ## getJob ### Description Retrieves detailed information about a specific job using its public token. ### Parameters #### Path Parameters - **publicToken** (string) - Required - The unique public token of the job. ### Returns - **Promise** - A promise that resolves to the detailed job object. ``` -------------------------------- ### Server-side Quickstart with Secret Key Source: https://github.com/startupkit-app/jobs-js/blob/main/README.md Use a secret key for server-side operations to list jobs and retrieve full job details. Supports pagination and iteration over all jobs. ```ts import { createClient } from "@startupkit/jobs"; const kit = createClient({ secretKey: process.env.KIT_SECRET_KEY }); // One page at a time const page = await kit.listJobs({ department: "Engineering", remote: true }); for (const job of page.data) { console.log(job.title, job.location, job.url); } if (page.hasNextPage) { const next = await page.nextPage()!; } // Or iterate everything for await (const job of kit.allJobs()) { console.log(job.id, job.title); } // Full detail: description, stages, and the application form definition const job = await kit.getJob(page.data[0].id); console.log(job.description_html, job.accepting_applications); ``` -------------------------------- ### Resume Upload Flow Source: https://github.com/startupkit-app/jobs-js/blob/main/README.md Upload files directly to storage and get a signed ID for applications. Supports optional client-side validation against job constraints. ```ts const file = fileInput.files[0]; // or a Blob in Node // Optional client-side validation against the job's constraints: const { content_types, max_byte_size } = job.application_form.resume; const { signed_id } = await kit.uploadFile(file); await kit.apply( job.id, { email: "jane@example.com", resume_signed_id: signed_id, } ); ``` -------------------------------- ### Browser Apply Logic with @startupkit/jobs Source: https://github.com/startupkit-app/jobs-js/blob/main/examples/browser-apply.html This snippet initializes the client, fetches job details, renders the form dynamically, and handles application submission, including optional resume uploads and Turnstile integration. It also includes error handling for API responses and network issues. ```javascript import { createClient, KitApiError } from "https://esm.sh/@startupkit/jobs@0.1.0"; const PUBLISHABLE_KEY = "pk_live_REPLACE_ME"; // pk_ keys are browser-safe const JOB_TOKEN = new URLSearchParams(location.search).get("job") ?? "REPLACE_ME"; const kit = createClient({ publishableKey: PUBLISHABLE_KEY }); const form = document.getElementById("apply-form"); const status = document.getElementById("status"); async function initializeForm() { try { const job = await kit.getJob(JOB_TOKEN); document.getElementById("job-title").textContent = job.title; document.getElementById("job-description").innerHTML = job.description_html; document.getElementById("consent").innerHTML = job.application_form.consent_disclosure_html; // Render screening questions const questionsEl = document.getElementById("questions"); for (const question of job.application_form.questions) { const label = document.createElement("label"); label.textContent = question.prompt + (question.required ? " *" : ""); const input = document.createElement("textarea"); input.name = `question:${question.key}`; input.required = question.required; input.maxLength = question.max_length; label.appendChild(input); questionsEl.appendChild(label); } // Render Turnstile when the job requires it let turnstileToken; if (job.application_form.turnstile.required) { window.turnstile?.render("#turnstile-widget", { sitekey: job.application_form.turnstile.sitekey, callback: (token) => { turnstileToken = token; }, }); } if (job.accepting_applications) { form.hidden = false; } else { status.textContent = "This job is no longer accepting applications."; } } catch (error) { status.textContent = "Failed to load job details."; console.error(error); } } initializeForm(); form.addEventListener("submit", async (event) => { event.preventDefault(); status.textContent = "Submitting…"; const data = new FormData(form); try { // 1. Upload the resume (optional) let resume_signed_id; const resume = data.get("resume"); if (resume instanceof File && resume.size > 0) { status.textContent = "Uploading resume…"; ({ signed_id: resume_signed_id } = await kit.uploadFile(resume)); } // 2. Collect screening question answers const responses = {}; const jobDetails = await kit.getJob(JOB_TOKEN); // Re-fetch job to get question keys for (const question of jobDetails.application_form.questions) { const answer = data.get(`question:${question.key}`); if (answer) { responses[question.key] = String(answer); } } // 3. Submit the application const result = await kit.apply( JOB_TOKEN, // Use JOB_TOKEN directly as it's the job ID { email: String(data.get("email")), first_name: String(data.get("first_name") || "") || undefined, last_name: String(data.get("last_name") || "") || undefined, responses, resume_signed_id, }, { turnstileToken } // Pass turnstileToken here ); status.textContent = `Application ${result.id} submitted. Good luck!`; form.querySelector("button").disabled = true; } catch (error) { if (error instanceof KitApiError) { if (error.code === "already_applied") { status.textContent = "You have already applied to this job."; } else if (error.fields) { status.textContent = Object.entries(error.fields) .map(([field, messages]) => `${field}: ${messages.join(", ")}`) .join(" · "); } else { status.textContent = error.message; } } else { status.textContent = "Network error — please try again."; } console.error(error); } }); ``` -------------------------------- ### Client Initialization Source: https://github.com/startupkit-app/jobs-js/blob/main/README.md Creates an instance of the KitJobsClient. Use a `secretKey` for server-side operations and a `publishableKey` for client-side operations. The `baseUrl` can be optionally provided to point to a different API endpoint. ```APIDOC ## createClient ### Description Initializes the KitJobsClient with API keys and optional base URL. ### Parameters - **publishableKey** (string) - Optional - Your publishable API key (`pk_…`). Use for client-side operations. - **secretKey** (string) - Optional - Your secret API key (`sk_…`). Use for server-side operations. - **baseUrl** (string) - Optional - The base URL for the Kit API. Defaults to the production API. ### Returns - **KitJobsClient** - An initialized client instance. ``` -------------------------------- ### Iterate All Jobs Source: https://github.com/startupkit-app/jobs-js/blob/main/README.md Provides an asynchronous iterator to easily loop through all published jobs without manual pagination. ```APIDOC ## allJobs ### Description Returns an async iterable that yields all jobs, handling pagination automatically. ### Returns - **AsyncIterable** - An asynchronous iterator yielding `Job` objects. ``` -------------------------------- ### Upload File Source: https://github.com/startupkit-app/jobs-js/blob/main/README.md Handles the complete file upload process, including checksum computation, registration, direct storage upload, and returning a signed ID. ```APIDOC ## uploadFile ### Description Uploads a file directly to storage and returns its signed ID. This method computes the MD5 checksum, registers the blob, performs the `PUT` request, and returns the `signed_id`. ### Parameters - **file** (File | Blob) - Required - The file to upload. - **meta** (object) - Optional - Additional metadata for the upload. (Specific fields not detailed in source). ### Returns - **Promise<{ signed_id: string }>** - A promise that resolves to an object containing the `signed_id` of the uploaded file. ``` -------------------------------- ### Create Upload Ticket Source: https://github.com/startupkit-app/jobs-js/blob/main/README.md Registers a file for upload and returns the necessary information to perform a direct upload to storage. ```APIDOC ## createUpload ### Description Initiates the file upload process by registering the blob metadata. Returns the URL and headers required for a direct `PUT` request to storage. ### Parameters #### Request Body - **meta** (object) - Required - Metadata about the file to be uploaded. (Specific fields not detailed in source). ### Returns - **Promise** - A promise that resolves to an `UploadTicket` object containing the upload URL and headers. ``` -------------------------------- ### Apply for Job Source: https://github.com/startupkit-app/jobs-js/blob/main/README.md Submits an application for a specific job. Can include applicant details, responses to questions, and optionally a Turnstile token and resume signed ID. ```APIDOC ## apply ### Description Submits an application for a given job. This method accepts applicant details, answers to custom questions, and optional Turnstile verification tokens or resume upload IDs. ### Parameters #### Path Parameters - **publicToken** (string) - Required - The public token of the job to apply for. #### Request Body - **input** (ApplicationInput) - Required - An object containing applicant details, such as `email`, `first_name`, `last_name`, `responses` to questions, and `resume_signed_id`. - **opts** (object) - Optional - Options for the application, such as `turnstileToken`. - **turnstileToken** (string) - Optional - The token obtained from the Cloudflare Turnstile widget. ### Returns - **Promise** - A promise that resolves to the result of the application submission. ``` -------------------------------- ### List Jobs Source: https://github.com/startupkit-app/jobs-js/blob/main/README.md Retrieves a paginated list of published jobs. Supports filtering by department and remote status. ```APIDOC ## listJobs ### Description Fetches a paginated list of jobs. You can filter jobs by specifying `department` and `remote` parameters. ### Parameters #### Query Parameters - **department** (string) - Optional - Filters jobs by department. - **remote** (boolean) - Optional - Filters jobs to include only remote positions. ### Returns - **Promise>** - A promise that resolves to a `Page` object containing job data and pagination information. ``` -------------------------------- ### Error Handling with KitApiError and KitNetworkError Source: https://github.com/startupkit-app/jobs-js/blob/main/README.md Catch and handle specific API errors like 'already_applied' or 'validation_failed', and network errors. ```ts import { KitApiError, KitNetworkError } from "@startupkit/jobs"; try { await kit.apply(job.id, { email }); } catch (error) { if (error instanceof KitApiError) { switch (error.code) { case "already_applied": // 409 break; case "validation_failed": // 422 — see error.fields console.log(error.fields); // { email: ["is invalid"], … } break; case "turnstile_failed": // 422 — refresh the widget and retry break; default: console.error(error.status, error.code, error.message); } } else if (error instanceof KitNetworkError) { // retry / offline UI } } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.