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/v1Submit 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.
| Field | Needed | Notes |
|---|---|---|
title | required | Job title. |
description | required | What the person will do and what they need. At least 80 characters. |
location | required | Town or area, or "Remote, Ireland". |
work_arrangement | required | onsite, hybrid or remote. |
employment_type | required | Full-time, Part-time, Contract, Temporary or Internship. |
salary_min, salary_max | required | Numbers in euro. min ≤ max. No "DOE". |
salary_period | required | year, month, week, day or hour. Never converted. |
salary_basis | required | base, ote or commission_only. |
apply_method | required | email (applications come through role.ie) or url (your own page). |
apply_email / apply_url | one required | Where applications go. apply_email is never shown publicly. |
closing_date | optional | YYYY-MM-DD, within a year. Otherwise listed for 30 days. |
external_ref | optional | Your own vacancy id. Re-sending the same one never creates a duplicate. |
advertiser_type, hiring_company | agencies | recruitment_agency plus the employer you are hiring for. |
contact_name, contact_phone, company_website | optional | For 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());