API reference
Post jobs from your ATS
Use the Jobby.dev REST API to create live events and attach roles directly from your ATS (Greenhouse, Ashby, Lever).
Last updated
If your team manages roles in Greenhouse / Ashby / Lever, you can push them straight to Jobby.dev whenever they go live — no copy-paste between tools. ~30 minutes; the heaviest lift is mapping ATS fields to Jobby.dev role fields.
Architecture
Two flows wired together:
- ATS → Jobby.dev when a role opens. We create a Jobby.dev role and attach it to your next live event window.
- Jobby.dev → ATS when a candidate advances. The Merge-powered ATS sync (recruiter integrations) handles this leg automatically once you've authorized the integration.
1. ATS → Jobby.dev: trigger
Three options depending on your ATS:
- Greenhouse— "Job Created" webhook in the Greenhouse webhooks UI.
- Ashby— "Job Posted" webhook in Ashby's integrations.
- Lever— Lever doesn't emit a job-created webhook; poll their
/v1/postingsendpoint on a 5-minute cron and diff against last-known state.
2. Map ATS fields to Jobby.dev role fields
The mapping isn't 1:1; here's what to plumb:
| ATS field | Jobby.dev role field | Notes |
|---|---|---|
| Job title | title | 1:1. |
| Job posting body | description | Strip HTML; collapse whitespace. |
| Skills / tags | required_skills | Trim to 3-7 strongest tags. |
| Salary range | compensation_min + _max | Convert to USD if needed. |
| Location | location_city + _country | Parse out remote / hybrid signal separately. |
| Visa sponsorship | visa_sponsorship | Yes / no / case-by-case. |
3. Create the role on Jobby.dev
async function createJobbydevRole(atsJob) {
const role = mapAtsToJobbydev(atsJob);
const res = await fetch("https://jobby.dev/api/v1/jobs", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.JOBBYDEV_API_TOKEN}`,
"Content-Type": "application/json",
"Idempotency-Key": `ats-${atsJob.id}`,
},
body: JSON.stringify(role),
});
if (!res.ok) throw new Error(await res.text());
const { id: jobbydevRoleId } = await res.json();
// Persist the mapping so subsequent updates target the same role
await db.atsJobMapping.upsert({
ats_id: atsJob.id,
jobbydev_role_id: jobbydevRoleId,
});
}Note the Idempotency-Key— re-firing the webhook from your ATS won't create duplicate roles.
4. Attach to a live event
Roles need to be attached to an event to be visible to seekers. Two patterns:
- Auto-attach to the next scheduled event. Simplest. The team's next live event picks up everything new since last live.
- Per-role event. Power Play tier — every role gets a dedicated 1-hour window. Higher signal, more scheduling load.
5. Update + delete
Subscribe to your ATS's job-updated and job-closed events. Mirror via PATCH /api/v1/jobs/{id} and DELETE /api/v1/jobs/{id} using the mapping from step 3.
6. Test
Open a test role in your ATS; confirm it appears on Jobby.dev within ~10 seconds. Close it; confirm it disappears.
Production hardening
- Rate limits: bulk imports may exceed 30 writes/minute. Throttle to one role per 2 seconds during initial backfill.
- PII: never copy candidate data from your ATS into a Jobby.dev role. Roles are public; they shouldn't carry private notes or candidate identifiers.
- Audit: log every Jobby.dev API call your bridge makes — useful when reconciling discrepancies.
Related reading
- Recruiter integrations.
- Error codes — esp.
VALIDATION.