Quick Start: Create a job

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:

  • globalCode must be unique across all jobs in the portal. A clash is 400 GLOBAL_CODE_ALREADY_EXISTS.
  • titles must contain an entry for defaultCultureCode. 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 customOus entries sharing an ouTypeId are 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 changeUse
Code, titles, descriptions, custom fieldsPATCH v1/jobs/{jobId}
Active or inactivePOST v1/jobs/{jobId}/statuses
Job function, job level, custom OUPOST to the matching assignment collection
Critical flag and its notesPATCH 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: false from 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 with 400 JOB_IS_ASSIGNED_TO_POSITIONS.

Deactivate when a role is dormant. Archive only when it should disappear.