Coding agent documentation index Fetch the complete documentation index at: https://docs.worklittle.com/docs-agent-manifest.json Use this file to discover all available pages before exploring further.

SDK

TypeScript and Python clients for api.worklittle.com. Same key, same resources. Language tabs below.

Install

Requires Node 18+.

npm install worklittle
export WORKLITTLE_API_KEY="sk-wl-api01-..."

The same package is also published as @worklittle/sdk.

Authentication

Send your key as a bearer token. Create one under API keys in Worklittle Business.

Authorization: Bearer sk-wl-api01-...

The SDKs and CLI read WORKLITTLE_API_KEY. Prefer the env var over --api-key, which is visible in process lists.

Public job board reads and public survey submit need no key.

Quickstart

import { createWorklittle } from "worklittle";

const wl = createWorklittle(); // reads WORKLITTLE_API_KEY
const page = await wl.jobs.search({ q: "software engineer", limit: 3 });
console.log(page.data);

Filters match GET /jobs. Prefix a value with - to exclude it:

await wl.jobs.search({
  title: "software engineer, -senior",
  company: "-lucid-motors,-tesla",
  limit: 10,
});

Client options: apiKey, baseUrl (default https://api.worklittle.com), fetch, maxRetries (default 3). Your key is sent to baseUrl, so only point it at hosts you trust.

Search > detail

const page = await wl.jobs.search({ q: "software engineer", limit: 3 });
const [first] = page.data as Array<{ job_id: string }>;
const job = await wl.jobs.get(first.job_id);

GET /jobs returns:

{
  "data": [
    {
      "job_id": "...",
      "title": "Software Engineer",
      "company_name": "Example Co",
      "location": "Remote"
    }
  ],
  "meta": { "next_cursor": "..." }
}

List rows are snippets. jobs.get returns the full posting. Pass meta.next_cursor as cursor for the next page. See Search jobs.

Board > apply

YOUR_ORG_SLUG is the employer slug in your careers URLs.

import { createWorklittle, type BoardJob } from "worklittle";

const wl = createWorklittle();
const company = "YOUR_ORG_SLUG";

const board = (await wl.boards.get(company)) as { jobs: BoardJob[] };
const ref = board.jobs[0].slug || board.jobs[0].id;
// The response wraps the job: { company, job, features, meta }.
const { job } = (await wl.boards.getJob(company, ref)) as { job: BoardJob };

// Splits `values` into built-in fields and custom answers using job.application_fields.
await wl.boards.submitApplication(company, job, {
  name: "Alex Rivera",
  email: "alex@example.com",
});

wl.boards.apply(company, ref, body) posts a raw body instead. When the board has similar-job alerts on, the body must include similar_job_alerts as true or false. See Embedded job board.

Candidates > profile

Requires a key with the jobs:applications scope.

const list = await wl.candidates.list({ limit: 10 });
const candidate = await wl.candidates.get("CANDIDATE_ID");

See Manage candidates.

Resources

| Method | HTTP | Docs |
| --- | --- | --- |
| `wl.boards.get` / `listJobs` / `getJob` / `apply` / `submitApplication` | `/business/jobs/company/:company/*` | [Public job board](/business/api/public-job-board) |
| `wl.jobs.search` / `get` / `map` / `salaryAverage` / `suggestions` / `stats` / `saved` | `GET /jobs`, `/jobs/:id`, `/jobs/map`, `/jobs?salary_average=1`, `/jobs/suggestions`, `/jobs/stats`, `/jobs/saved` | [Search jobs](/business/api/search-jobs) |
| `wl.companies.search` | `GET /companies` | [Search companies](/business/api/search-companies) |
| `wl.stats.get` | `GET /jobs/stats` | [Search jobs](/business/api/search-jobs) |
| `wl.apply.submit` | `POST /jobs/:id/apply` | [Apply for jobs](/business/api/apply-for-jobs) |
| `wl.apply.startWithAi` / `getWithAiSession` / `continueWithAi` / `approveWithAiSubmit` / `stopWithAi` (also `start` / `status` / `stop`) | `POST /jobs/apply`, `GET /jobs/apply/:id`, `POST /jobs/apply/:id/stop`, `POST /v1/apply-with-ai/:id/continue`, `/v1/apply-with-ai/:id/approve-submit` | [Apply for jobs](/business/api/apply-for-jobs) |
| `wl.appliedJobs.*` | `/jobs/applied`, `/jobs/saved` (`counts`) | [Apply for jobs](/business/api/apply-for-jobs) |
| `wl.resumes.*` / `wl.coverLetters.*` | `POST /jobs/resumes`, `/jobs/cover-letters`, `/v1/draft-resumes`, `/v1/draft-cover-letters` (`draft`) | [Create resumes](/business/api/create-resumes) |
| `wl.agent.listTools` / `runTool` | `/v1/agent/tools`, `/v1/agent/tool` | [MCP](/business/mcp) |
| `wl.candidates.*` | `/candidates`, `/pipeline-stages` | [Manage candidates](/business/api/manage-candidates) |
| `wl.employees.*` / `wl.organization.*` | `/employees`, `/company-organizations` | [Manage organization](/business/api/manage-organization) |
| `wl.jobListings.*` | `/business/jobs` | [Post a job](/business/api/post-a-job) |
| `wl.employerJobs.*` | `/jobs/post` | [Post a job](/business/api/post-a-job) |
| `wl.surveys.*` | `/platform/surveys` | [Surveys](/business/api/surveys) |
| `wl.documents.*` | `/platform/documents` | [Documents](/business/api/documents) |
| `wl.attendance.*` | `/attendance`, `/business/attendance` | [Attendance](/business/api/attendance) |
| `wl.webhooks.*` | `/webhooks`, `/webhook-deliveries` | [Webhooks](/business/webhooks/integration) |
| `wl.email.*` / `wl.offers.*` | `/email-messages`, `/email/send`, `/offers` | [Manage candidates](/business/api/manage-candidates) |
| `wl.usage.list` / `wl.billing.*` / `wl.limits.*` | `/usage`, `/billing/*`, `/limits` | [API keys](/business/get-started/api-keys) |

Every method returns the parsed JSON body. Query filters match the REST parameters.

Webhooks

import { verifyWebhookSignature } from "worklittle";

const ok = verifyWebhookSignature({
  signatureHeader: req.headers["worklittle-webhook-signature"] as string,
  timestamp: req.headers["worklittle-webhook-timestamp"] as string,
  rawBody, // the unparsed request body string
  secret: process.env.WORKLITTLE_WEBHOOK_SECRET!,
});

Rejects timestamps more than 300 seconds old (toleranceSeconds to change). See Webhook integration.

Errors

import { WorklittleError, PaymentRequiredError } from "worklittle";

try {
  await wl.jobs.get("JOB_ID");
} catch (err) {
  if (err instanceof PaymentRequiredError) {
    // 402: fix billing before retrying
  } else if (err instanceof WorklittleError) {
    console.error(err.status, err.code, err.message, err.requestId);
  }
}
| Status | Error class | Behavior |
| --- | --- | --- |
| 429 | `WorklittleError` | Retried up to `maxRetries`, waiting `Retry-After` |
| 429 + `QUOTA_EXCEEDED` | `QuotaExceededError` | Monthly free quota used. Retried like any 429, then raised |
| 402 | `PaymentRequiredError` | Not retried. Do not retry until billing is fixed |
| 401 / 403 | `WorklittleError` | Invalid key or missing scope |

QuotaExceededError and PaymentRequiredError extend WorklittleError. Errors also carry documentationUrl and body.