API reference
Jobby.dev REST API
Stable v1 REST API for building agent integrations on top of Jobby.dev. Bearer-token auth, predictable error envelope, OpenAPI 3.1.
Last updated
Short answer: the Jobby.dev REST API is a stable v1 surface (~35 endpoints) for building agent integrations. Auth is bearer-token, errors are a uniform { error, code, details? } envelope, every authed response carries X-RateLimit-* headers, and the OpenAPI 3.1 spec at /api/v1/openapi.json is the source of truth.
Base URL
https://jobby.dev/api/v1All paths in this reference are relative to that base. The v1 surface is committed to: breaking changes go to /api/v2, never to/api/v1.
Authentication
Mint a Personal Access Token at jobby.dev/account/api-tokens and pass it as a bearer token:
curl https://jobby.dev/api/v1/live-events \
-H "Authorization: Bearer jbb_..."Tokens are prefixed jbb_ and are scoped — when minting, select only the scopes the integration needs. The full scope list:
profile:read,profile:writequeue:read,queue:writematches:read,matches:writebilling:read,billing:writeevents:read,events:write(recruiter)jobs:read,jobs:write(recruiter)candidates:read,candidates:write(recruiter)webhooks:read,webhooks:writesettings:read,settings:write
A token cannot accept a match or join the live video room. That tap is always human-driven — see on-demand job fair.
Errors
Every non-2xx response carries the same envelope:
{
"error": "Off-session charges require one-time consent. ...",
"code": "OFF_SESSION_CONSENT_REQUIRED",
"details": { "target_plan": "pro", "interval": "monthly" }
}error is always present and human-readable. code is a stable machine-readable identifier (use this for branching). details is type-specific. Common codes:
VALIDATION— request body failed schema checkRATE_LIMITED— seeRetry-AfterINSUFFICIENT_SCOPE— token doesn't carry the scope for this endpointNOT_FOUND— resource doesn't exist orcaller isn't the owner (existence isn't leaked across tenants)DEPENDENCY_UNAVAILABLE— upstream (Stripe, Daily, Anthropic) unreachable; safe to retry
Rate limits
Per-token limits vary by endpoint cost. Cost-bearing endpoints (off- session charge, AI compare, room URL issuance) cap at 3/min; pure reads are 60/min. Every successful and failed response carries:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 47
X-RateLimit-Reset: 1714780000On 429, also Retry-After: <seconds>. Implement exponential backoff bounded by the reset timestamp.
OpenAPI
The OpenAPI 3.1 spec is the canonical reference and is hand-curated:
- JSON: /api/v1/openapi.json
A CI invariant (scripts/check-openapi-drift.mjs) asserts every route file under apps/web/src/app/api/v1/ has a matching paths entry — adding a route without registering it fails the build.
SDKs
- JavaScript / TypeScript —
@jobbydev/sdk(zero deps, Node 20+ / browsers / Workers / Bun / Deno). - Python —
jobbydev(httpx, Python 3.10+).
Both SDKs are scaffold-only today. Publish gate is the post-Tier 8 audit on the v1 surface.
Versioning + deprecation policy
The v1 surface is a forward contract. We commit to:
- No removed endpoints on v1.
- No removed response fields on v1.
- No renamed error codes on v1.
- No tightened request validation that rejects payloads that previously worked.
Additive changes — new endpoints, new optional request fields, new response fields, new error codes — ship freely on v1. Major version bumps go to /api/v2 with a parallel old/new period of at least 90 days.
Endpoints we plan to remove (rare) emit a Deprecation: true header on every call plus a Sunset: <date> header indicating when the endpoint disappears from /api/v2. Clients should log Deprecation headers and migrate before the sunset date.
Idempotency
Mutating endpoints accept an optional Idempotency-Key request header carrying a UUID v4. If the same key is replayed within 24 hours, the original response is returned — including the original status code and body — without re-applying the side effect.
curl https://jobby.dev/api/v1/billing/charge-offsession \
-H "Authorization: Bearer jbb_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
-d '{"target_plan": "pro", "interval": "monthly"}'Use this for any retry loop on a non-idempotent endpoint (off-session charge, match decline, event create). Cost-bearing endpoints additionally bucket idempotency by tokenId / resource / interval automatically as a defense-in-depth — see the inline comment on charge-offsession for the bucketing key.
End-to-end worked example
Mint a token at /account/api-tokens with scopes queue:write, queue:read, matches:read. Then:
# 1. Find a live event
curl -s 'https://jobby.dev/api/v1/live-events?limit=5' | jq '.data[0]'
# Suppose this returns event slug "evt_abc123"
# 2. Join its queue (authenticated)
curl https://jobby.dev/api/v1/queue/join \
-H "Authorization: Bearer $JOBBYDEV_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"event_id": "evt_abc123"}'
# → 200 OK, queue position returned
# 3. Poll status (every ~5 seconds, not faster)
curl -s https://jobby.dev/api/v1/queue/status \
-H "Authorization: Bearer $JOBBYDEV_API_TOKEN" | jq
# → eventually shows status: "matched" with match_id
# 4. Open the live room (the user does this in their browser)
echo "Open https://jobby.dev/account/matches/<match_id> to accept"Step 4 is intentionally browser-only — see the humans-only rule. An agent can fetch room URL via GET /api/v1/matches/{id}/interview-room-url after acceptance, but acceptance itself is session-authed.
Related reading
- MCP server overview — same functionality, conversational interface.
- What is an on-demand job fair? — the product DNA the API encodes.