Quick Start: Create a job
TIP
View the Job Management API Reference
A job can be created complete in a single call. POST v1/jobs accepts the job's identity, its localized titles, its initial status and its initial job function, job level and custom OU assignments together, so you do not have to create a bare job and then attach structure to it.
This walkthrough creates a fully classified job, then shows how to change it afterwards.
Step 1: Collect the identifiers you will reference
The job's classification points at existing records, so fetch those first. All three are optional on create, but supplying them is what makes the job usable.
GET /services/api/x/chr-jobmgmt-app/v1/job-functions?titleOrCode=Engineering
GET /services/api/x/chr-jobmgmt-app/v1/job-levels?titleOrCode=Senior
GET /services/api/x/chr-jobmgmt-app/v1/custom-ou-types
GET /services/api/x/chr-jobmgmt-app/v1/custom-ou-types/{ouTypeId}/ous?titleOrCode=Berlin
Note the custom OU flow needs two calls: list the OU types, then list the OUs within the type you want. A custom OU is addressed by type and identifier together.
Records must be active and non-archived
A job function, job level or custom OU that has been archived cannot be assigned. Archived records never appear in these collections, so anything you find here is assignable.
Step 2: Decide the effective date
Everything in the create payload takes effect on one dateEffective. It may be in the past, to backfill a job that already existed, or in the future, to stage a job before it opens.
The date is a calendar date, YYYY-MM-DD, in the portal's time zone.
Step 3: Create the job
POST /services/api/x/chr-jobmgmt-app/v1/jobs
Content-Type: application/json
{
"globalCode": "ENG-SE-001",
"defaultCultureCode": "en-US",
"titles": [
{ "cultureCode": "en-US", "value": "Software Engineer" },
{ "cultureCode": "de-DE", "value": "Softwareentwickler" }
],
"descriptions": [
{ "cultureCode": "en-US", "value": "Builds and maintains product software." }
],
"active": true,
"dateEffective": "2026-01-01",
"jobFunctionId": "a1b2c3d4-0000-0000-0000-000000000001",
"jobLevelId": "a1b2c3d4-0000-0000-0000-000000000002",
"customOus": [
{ "ouTypeId": 1, "customOuId": "a1b2c3d4-0000-0000-0000-000000000003" }
],
"customFields": { "42": "Cost centre 1200" }
}
Five members are required: globalCode, defaultCultureCode, titles, active and dateEffective.
Three rules are worth knowing before your first call:
globalCodemust be unique across all jobs in the portal. A clash is400 GLOBAL_CODE_ALREADY_EXISTS.titlesmust contain an entry fordefaultCultureCode. That culture is the fallback when a caller asks for a culture with no title.- A job holds at most one job function and one job level, and at most one custom OU per OU type. Two
customOusentries sharing anouTypeIdare rejected.
HTML descriptions come later
descriptions takes plain text only. HTML descriptions cannot be set at create time; add them afterwards with PATCH v1/jobs/{jobId}.
The response returns the new jobId and its URI, also sent in the Location header.
Step 4: Verify what you created
GET /services/api/x/chr-jobmgmt-app/v1/jobs/{jobId}
displayTitle comes back resolved to your culture, falling back to defaultCultureCode. It is read-only and derived, so to change a title you write the titles set, never displayTitle.
To see the assignments the create call made, read the timelines. Each one has a single entry effective from your dateEffective:
GET .../v1/jobs/{jobId}/statuses
GET .../v1/jobs/{jobId}/job-functions
GET .../v1/jobs/{jobId}/job-levels
GET .../v1/jobs/{jobId}/custom-ous
List responses omit expensive fields
GET v1/jobs excludes titles, descriptions, htmlDescriptions and customFields from its default field set. Request them through fields when you need a full export. The single-job read returns the full representation.
Step 5: Change the job afterwards
Once the job exists, the create payload's shortcuts no longer apply. Each concern has its own route:
| To change | Use |
|---|---|
| Code, titles, descriptions, custom fields | PATCH v1/jobs/{jobId} |
| Active or inactive | POST v1/jobs/{jobId}/statuses |
| Job function, job level, custom OU | POST to the matching assignment collection |
| Critical flag and its notes | PATCH v1/jobs/{jobId} with classification |
The assignment routes add a dated entry rather than overwriting. The previous assignment stays on the timeline and ends the day before the new one starts, so the job's history is preserved. See Effective dating for how that works and which date rules apply.
PATCH uses JSON Merge Patch, so send Content-Type: application/merge-patch+json and include only the members you are changing.
Step 6: Assign the job to someone
A job with no holders is still just a definition. See User job assignments.
Taking a job out of service
Two different things, easy to confuse:
- Deactivating posts a status assignment with
active: falsefrom a date. The job still exists and keeps its history. - Archiving is
DELETE v1/jobs/{jobId}, a soft delete that removes the job from reads and lists with no way to restore it. A job still assigned to positions is refused with400 JOB_IS_ASSIGNED_TO_POSITIONS.
Deactivate when a role is dormant. Archive only when it should disappear.