Posting API

Send vacancies from your ATS, close or update them, and get applications as signed webhooks. The rules are the web form’s rules: every listing needs a real pay range with its unit and basis, and every listing is reviewed.

Authentication

Create a key under API and integrations in your dashboard. Send it on every request. Keys are shown once, stored only as a hash, and can be revoked at any time. Limit: 300 requests per hour per key.

Authorization: Bearer rk_live_…
Base URL: https://www.role.ie/api/role/v1

Submit a vacancy

POST /jobs → 201 with the submission. Most listings are reviewed and published within minutes; any listing whose pay looks unusual is held for a person to check.

curl -X POST https://www.role.ie/api/role/v1/jobs \
  -H "Authorization: Bearer $ROLE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "external_ref": "REQ-1042",
    "title": "Payroll Administrator",
    "description": "Run fortnightly payroll for 400 staff across three sites…",
    "location": "Dundalk, Co. Louth",
    "work_arrangement": "hybrid",
    "employment_type": "Full-time",
    "salary_min": 38000, "salary_max": 44000,
    "salary_period": "year", "salary_basis": "base",
    "apply_method": "email", "apply_email": "hiring@example.ie",
    "closing_date": "2026-12-15"
  }'
{ "submission": { "id": "…", "external_ref": "REQ-1042", "status": "published",
                 "job_id": "…", "job_url": "https://www.role.ie/jobs/…" } }

status is published, pending, pending_review (held for a person) or rejected. A validation failure returns 422 with a message per field.

FieldNeededNotes
titlerequiredJob title.
descriptionrequiredWhat the person will do and what they need. At least 80 characters.
locationrequiredTown or area, or "Remote, Ireland".
work_arrangementrequiredonsite, hybrid or remote.
employment_typerequiredFull-time, Part-time, Contract, Temporary or Internship.
salary_min, salary_maxrequiredNumbers in euro. min ≤ max. No "DOE".
salary_periodrequiredyear, month, week, day or hour. Never converted.
salary_basisrequiredbase, ote or commission_only.
apply_methodrequiredemail (applications come through role.ie) or url (your own page).
apply_email / apply_urlone requiredWhere applications go. apply_email is never shown publicly.
closing_dateoptionalYYYY-MM-DD, within a year. Otherwise listed for 30 days.
external_refoptionalYour own vacancy id. Re-sending the same one never creates a duplicate.
advertiser_type, hiring_companyagenciesrecruitment_agency plus the employer you are hiring for.
contact_name, contact_phone, company_websiteoptionalFor our reviewers only.

List and manage

GET   /jobs                  your listings and submissions awaiting review
GET   /jobs/{id}             one listing   ({id} = role.ie job id, or ref:REQ-1042)
PATCH /jobs/{id}  { "action": "close" }       take it down now
PATCH /jobs/{id}  { "action": "filled" }      take it down, marked filled
PATCH /jobs/{id}  { "action": "reconfirm" }   still open: list for another 30 days
PATCH /jobs/{id}  { "action": "salary", "min": 40000, "max": 46000,
                    "period": "year", "basis": "base" }

Application webhook

Set an https URL in your dashboard. When someone applies through role.ie to one of your listings, we POST:

POST https://your-ats.example/hooks/role
X-Role-Event: application.created
X-Role-Signature: t=1762450000,v1=5f0c…

{ "event": "application.created", "created_at": "…",
  "data": { "application_id": "…", "job_id": "…", "job_title": "…",
            "applicant_name": "…", "applicant_email": "…",
            "cover_note": "…", "has_cv": true, "applied_at": "…" } }

Verify it: compute HMAC-SHA256 of `${t}.${rawBody}` with your signing secret and compare it to v1; reject anything older than five minutes. CVs are not sent in the webhook — they are attached to the application email and available in your dashboard. One attempt, five-second timeout: the application is always emailed as well, so a missed webhook loses nothing.

// Node
const [t, v1] = sig.split(',').map(p => p.split('=')[1]);
const ok = crypto.timingSafeEqual(
  Buffer.from(v1, 'hex'),
  crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest());