You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
TL;DR Expose jobs and organization pages through /api/2.0 (read for everyone with a token, write for a posting's owner), add a job.published webhook topic, and document all of it in the developer docs — so HR tools can post and manage openings without the browser.
Order: 9/9 in the Jobs & Organizations milestone. Depends on #932 (resource + lifecycle), #933 (board filters), #929 (organizations). Where:VutuvWeb.ApiV2 (new controllers), router /api/2.0 scope, Vutuv.Webhooks, the /developers documentation pages.
Want — endpoints (additions only, per the API contract):
GET /api/2.0/jobs jobs:read list published postings; same filters
as /jobs (q, tag, near + radius,
country, salary_min, workplace_type,
employment_type); keyset-paginated
GET /api/2.0/jobs/:id jobs:read one posting incl. status + dates
GET /api/2.0/organizations jobs:read verified organizations, paginated, ?q=
GET /api/2.0/organizations/:slug jobs:read page incl. aliases + verified domains
POST /api/2.0/jobs jobs:write create draft or publish
PATCH /api/2.0/jobs/:id jobs:write edit own posting (same rules as the form)
POST /api/2.0/jobs/:id/closure jobs:write close (reason: filled | withdrawn)
DELETE /api/2.0/jobs/:id jobs:write discard a draft
Two new scopes jobs:read / jobs:write, available to personal access tokens and OAuth apps like the existing scopes.
The API is a second door to the same room, never a side channel: writes go through the exact Vutuv.Jobs changesets and policies as the forms — quotas, account-age gate, organization attribution only with a role, the fixed 90-day lifecycle (no renewal endpoint exists because no renewal exists), salary/AGG validations, rate limiter. An API posting is indistinguishable from a form posting.
Read responses of expired/closed postings keep working for the owner (their tooling needs the final state); the public token view returns published postings only.
Payloads carry the full structured location and pay data the board uses:street_address, zip_code, city, country, resolved coordinates when available, workplace_type, remote_countries, the complete salary block (required on publish, Jobs 5/9: Job postings: resource, 90-day lifecycle, detail page, easy apply #932), and the tags with their required/nice-to-have priority. Organization responses include the postal address. Publishing over the API enforces the same completeness as the form: an onsite/hybrid draft without zip + city + country, a remote draft without applicant countries, or a non-volunteer draft without a salary range gets a clean, documented validation error.
Webhooks: new subscribable topic job.published (payload: id, title, URL, employer name, structured location [city, zip code, country, workplace type, applicant countries for remote], salary range, tags) so integrators can mirror new openings with real filter data; delivery through the existing webhook machinery (SSRF-validated URLs, retries).
Documentation (part of the deliverable, not an afterthought):
/developers gains a "Jobs" chapter: auth + scopes, every endpoint with request/response examples, the lifecycle state machine and what each status means for API clients (including "expired postings cannot be edited or reopened — repost instead"), error catalogue (quota exceeded, attribution denied, editing an expired posting, incomplete location, missing salary range), webhook payload example.
docs/architecture/api.md and jobs.md updated; any new env knob lands in docs/ADMINS.md.
SEO & GEO: the JSON responses reuse the agent-doc structures where they exist (one doc map per page, no drift); machine-opt-out members' data never appears in API people fields beyond what the public page shows. Smoke test (curl + browser): create a PAT with jobs:write in the browser → POST /api/2.0/jobs a draft → try to publish it without a salary range and without a location → documented validation errors → complete it and publish via PATCH → posting appears on /jobs (browser), expires_on is 90 days out in the GET response and the response carries the structured location + salary → GET /api/2.0/jobs?near=Köln&radius=50&salary_min=60000 finds it → POST …/closure (reason filled) → status closed in the GET; a further PATCH returns a clean JSON error → job.published webhook delivery visible for a subscribed test endpoint → token without jobs:write gets 403 on write and 200 on read. Out of scope: applications over the API, people search over the API, OAuth scope UI changes beyond listing the new scopes.
TL;DR Expose jobs and organization pages through
/api/2.0(read for everyone with a token, write for a posting's owner), add ajob.publishedwebhook topic, and document all of it in the developer docs — so HR tools can post and manage openings without the browser.Order: 9/9 in the Jobs & Organizations milestone. Depends on #932 (resource + lifecycle), #933 (board filters), #929 (organizations).
Where:
VutuvWeb.ApiV2(new controllers), router/api/2.0scope,Vutuv.Webhooks, the/developersdocumentation pages.Want — endpoints (additions only, per the API contract):
jobs:read/jobs:write, available to personal access tokens and OAuth apps like the existing scopes.Vutuv.Jobschangesets and policies as the forms — quotas, account-age gate, organization attribution only with a role, the fixed 90-day lifecycle (no renewal endpoint exists because no renewal exists), salary/AGG validations, rate limiter. An API posting is indistinguishable from a form posting.street_address,zip_code,city,country, resolved coordinates when available,workplace_type,remote_countries, the complete salary block (required on publish, Jobs 5/9: Job postings: resource, 90-day lifecycle, detail page, easy apply #932), and the tags with their required/nice-to-have priority. Organization responses include the postal address. Publishing over the API enforces the same completeness as the form: an onsite/hybrid draft without zip + city + country, a remote draft without applicant countries, or a non-volunteer draft without a salary range gets a clean, documented validation error.job.published(payload: id, title, URL, employer name, structured location [city, zip code, country, workplace type, applicant countries for remote], salary range, tags) so integrators can mirror new openings with real filter data; delivery through the existing webhook machinery (SSRF-validated URLs, retries).Documentation (part of the deliverable, not an afterthought):
/developersgains a "Jobs" chapter: auth + scopes, every endpoint with request/response examples, the lifecycle state machine and what each status means for API clients (including "expired postings cannot be edited or reopened — repost instead"), error catalogue (quota exceeded, attribution denied, editing an expired posting, incomplete location, missing salary range), webhook payload example.docs/architecture/api.mdandjobs.mdupdated; any new env knob lands indocs/ADMINS.md.SEO & GEO: the JSON responses reuse the agent-doc structures where they exist (one doc map per page, no drift); machine-opt-out members' data never appears in API people fields beyond what the public page shows.
Smoke test (curl + browser): create a PAT with
jobs:writein the browser →POST /api/2.0/jobsa draft → try to publish it without a salary range and without a location → documented validation errors → complete it and publish viaPATCH→ posting appears on/jobs(browser),expires_onis 90 days out in theGETresponse and the response carries the structured location + salary →GET /api/2.0/jobs?near=Köln&radius=50&salary_min=60000finds it →POST …/closure(reasonfilled) → statusclosedin theGET; a furtherPATCHreturns a clean JSON error →job.publishedwebhook delivery visible for a subscribed test endpoint → token withoutjobs:writegets 403 on write and 200 on read.Out of scope: applications over the API, people search over the API, OAuth scope UI changes beyond listing the new scopes.