This help guide will show you how to create and update absences using the HR API Absence endpoint.
Contents
- Overview
- Before you start: API key configuration
- Step 1: Look up the IDs you need
- Step 2: Create an absence (POST)
- Step 3: Specifying individual days, hours and work patterns
- Step 4: Update an absence (PUT)
- Error messages and what they mean
- Good practice
Overview
To create an absence you need just four values: PerID, AbsCodeID, Start and End. The system calculates the days, hours and daily breakdown from the employee's work pattern. If you need to set the hours for each day yourself, for example for night shifts, you also send WorkPatternID and a Details line per day (Step 3).
This guide covers creating absences (POST) and updating them (PUT) through the Absence endpoint of the HR API. It sets out the API key configuration you need, the minimum payloads, worked examples, and what common error messages mean.
All examples use these headers, with <yoursystem> replaced by your own system address:
POST https://<yoursystem>.ciphr.com/api/Absence Authorization: apikey <your API key> Accept: application/json Content-Type: application/json
About the examples: values in angle brackets, such as <PerID> or <WorkPatternID>, are placeholders. IDs are unique to each system, so always look up your own (Step 1). |
Before you start: API key configuration
Your API key needs the Absence and AbsenceDetail entities, a data security filter and access to each absence type you use. The lookup entities are optional, and only needed if you find IDs through the API. For the full guide on how to configure the API key please review: API Configuration
| Setting | Required | What to enable | Error if missing |
|---|---|---|---|
| People filtering | Yes | A record group security filter covering the employees you will record absences for | No security filtering found |
| Absence Filtering | Yes | Each absence code you will use (for example Holiday, Sickness) | Access denied to this Absence Type |
Absence entity |
Yes | Create, Read and Update (Delete if needed) on the entity, plus the properties below | Unauthorized access to API end node for ... method |
AbsenceDetail entity |
Yes | Create, Read and Update on the entity and on all of its properties |
Unable to retrieve the data (see the warning below) |
PersonDetail entity |
Optional | Read Access, to look up WorkPatternID and PerID
|
Unauthorized access to API end node for GET method |
AbsenceCode entity |
Optional | Read Access, to look up AbsCodeID
|
Unauthorized access to API end node for GET method |
AbsenceReason entity |
Optional | Read Access, to look up AbsReasonID
|
Unauthorized access to API end node for GET method |
AllJobPay entity |
Optional | Read Access, to check the work pattern for past or future dates | Unauthorized access to API end node for GET method |
WorkPattern entity |
Optional | Read Access, to look up work patterns, including the 24-hour CIPHRFLEXI pattern | Unauthorized access to API end node for GET method |
Warning: if AbsenceDetail is not enabled, a POST returns 400 Unable to retrieve the data, but the absence is still saved. Retrying will create a duplicate. GET /Absence/{id}/WithDetail also fails, so updates cannot be made safely. |
Entity access and property access are set separately
In API Configuration (latest left-hand menu: System Configuration > API Configuration; pre 2025 top menu: System > Security > API), each entity (such as Absence or AbsenceDetail) has its own Create, Read, Update and Delete toggles. Expand the entity with the arrow to see its properties, which each have their own four toggles too. Access must be switched on at both levels. Enabling the AbsenceDetail entity without enabling its properties is not enough (see the screenshot below).
Which toggles each request uses:
| Toggle | Needed for |
|---|---|
| Create Access | POST |
| Read Access | GET, and the record returned after a POST or PUT |
| Update Access | PUT |
| Delete Access | DELETE (optional) |
Absence entity: properties to enable (Create, Read and Update)
| Property | Needed for |
|---|---|
PerID, AbsCodeID, Start, End
|
Every POST and PUT |
Details |
Individual days, part days, shift hours and every PUT |
WorkPatternID |
Whenever you send Details
|
ID |
PUT |
Notes |
Optional |
AbsReasonID |
Optional, for sickness reasons |
AbsenceDetail entity: properties to enable (Create, Read and Update)
Enable all of its properties: AbsenceID, ID, Start, End, Days, Hours, MinsLate, OSPPercent and OSPPreviousPercent. These hold each day of the absence. Without them, the daily lines in Details cannot be saved or returned.
If you use the optional lookups, enable ID, EmployeeNumber and WorkPatternID on PersonDetail; DateFrom, DateTo, WorkPatternID and WorkPatternDescription on AllJobPay (for past or future dates); ID, Code, Description, Type and AvgWeekHrs on WorkPattern; and ID, Code and Description on AbsenceCode, each with Read Access.
To check what your key can access, enter it on the Authentication tab of the API Help page (https://<yoursystem>.ciphr.com/api/Help). The Operations section then shows only the endpoints available to that key.
Step 1: Look up the IDs you need
The Absence endpoint only accepts IDs. Sending an employee number or absence code instead (for example EmployeeNumber or AbsenceCode) is rejected, so look these up first.
The lookups below need the optional PersonDetail and AbsenceCode entities on your key. If they are not enabled, ask your system administrator for the IDs.
Find the employee's PerID
GET https://<yoursystem>.ciphr.com/api/PersonDetail?EmployeeNumber=<EmployeeNumber>&f=ID,EmployeeNumber
{"PersonDetail":[{"ID":<PerID>,"EmployeeNumber":"<EmployeeNumber>"}]}The ID returned is the value to send as PerID.
Find the absence code's AbsCodeID
GET https://<yoursystem>.ciphr.com/api/AbsenceCode?f=ID,Code,Description
{"AbsenceCode":[{"ID":<AbsCodeID>,"Code":"H","Description":"Holiday"}, ...]}The ID returned is the value to send as AbsCodeID. To record a sickness reason, look up AbsReasonID in the same way from AbsenceReason.
Find the employee's WorkPatternID (only needed if you send Details)
GET https://<yoursystem>.ciphr.com/api/PersonDetail/<PerID>?f=ID,WorkPatternID
{"ID":<PerID>,"WorkPatternID":<WorkPatternID>}The WorkPatternID returned is the employee's current work pattern. Look it up just before each POST rather than storing it, so a recent change of pattern is picked up.
For an absence on a past or future date, check the employee's job history instead, as their pattern may be different on that date. PersonDetail only shows today's pattern, even when a future-dated change has been entered, and the API will accept an outdated WorkPatternID without warning. This needs Read Access on the AllJobPay entity, and the f parameter keeps the response to the work pattern fields only:
GET https://<yoursystem>.ciphr.com/api/AllJobPay?PerID=<PerID>&s=DateFrom&f=DateFrom,DateTo,WorkPatternID,WorkPatternDescription
Use the row whose DateFrom to DateTo range covers the absence's start date. A blank DateTo means the row is current. For example, an employee might be on a standard Monday to Friday pattern until 6 May, then on a night shift pattern from 7 May.
The WorkPattern entity (GET /api/WorkPattern/{id}) returns each pattern's description and type, and the standard start and end times for standard patterns. Daily shift times for shift patterns are not available through the API; your system administrator can provide them.
| Note: If an employee holds more than one post, each post has its own PerID under the same employee number. Use the PerID for the post the absence belongs to. |
Step 2: Create an absence (POST)
Send PerID, AbsCodeID, Start and End to POST /api/Absence. A successful request returns 201 Created with the full record, including its new ID.
| Field | Required | Format | Notes |
|---|---|---|---|
PerID |
Yes | Number | From PersonDetail |
AbsCodeID |
Yes | Number | From AbsenceCode |
Start |
Yes | YYYY-MM-DD |
First day of the absence |
End |
Yes | YYYY-MM-DD |
Last day; must be on or after Start
|
Details |
No | Array | One line per day, to set your own times and hours (see Step 3) |
WorkPatternID |
Only with Details
|
Number | The employee's work pattern ID; without it the pattern is left blank |
Notes |
No | Text | |
AbsReasonID |
No | Number | Not required, even for sickness codes |
When you don't send Details, the system builds the daily breakdown from the employee's work pattern. It sets Days, Hours and WorkPatternID, and skips non-working days. Top-level Days and Hours values you send are ignored; to set your own, use Details.
| Known limitations of the automatic calculation: public holidays are not excluded, and overnight shifts (for example 18:30–05:30) are calculated with negative hours. For overnight shifts, send the days and hours yourself. Where a public holiday falls within the absence, create two absences either side of it. Both are covered in Step 3. |
Example 1: Holiday over several days
{
"PerID": <PerID>,
"AbsCodeID": <AbsCodeID>,
"Start": "2027-05-10",
"End": "2027-05-12"
}Result: 3 days / 22.2 hours, with one detail line per working day (09:00–17:24 on a standard pattern). Non-working days are skipped automatically, so a Friday-to-Tuesday request records 3 days, not 5.
Example 2: Half day
Send a Details line with the times and the Days/Hours to record, and include the employee's WorkPatternID:
{
"PerID": <PerID>,
"AbsCodeID": <AbsCodeID>,
"WorkPatternID": <WorkPatternID>,
"Start": "2027-07-19",
"End": "2027-07-19",
"Details": [
{
"Start": "2027-07-19T09:00:00",
"End": "2027-07-19T12:42:00",
"Days": 0.5,
"Hours": 3.7
}
]
}Result: 0.5 days / 3.7 hours, linked to the employee's work pattern. The times must fall within the employee's working hours for that day. Putting times in the top-level Start and End instead does not work.
Example 3: Sickness with a reason
{
"PerID": <PerID>,
"AbsCodeID": <AbsCodeID>,
"AbsReasonID": <AbsReasonID>,
"Start": "2027-04-14",
"End": "2027-04-14",
"Notes": "Doctor's certificate received"
}
Step 3: Specifying individual days, hours and work patterns
Whenever you send your own Details lines, also send the employee's WorkPatternID. Without it, the absence saves but is not linked to any work pattern.
If you don't send Details, you don't need WorkPatternID: the employee's current work pattern is applied automatically. Sending a WorkPatternID other than the employee's own without Details fails with a 500 error.
Important: when you send Details, the API does not check that the WorkPatternID belongs to the employee. It checks your lines only against the pattern you send. A wrong ID is saved against the absence, so always use the value from PersonDetail. An ID that doesn't exist fails with a 500 error. |
Changes of work pattern: the automatic calculation uses the work pattern that applied on each day of the absence, taken from the employee's job history. This applies to past changes and to future-dated changes already entered in Ciphr HR. An absence that spans a change is calculated day by day, with each day on the pattern in force that day, and the absence records the pattern from its start date. When you send Details, use the WorkPatternID that applied on the absence's start date (see Step 1); if the pattern changes part-way through, create two absences either side of the change. |
Which approach to use
| Scenario | What to send |
|---|---|
| Standard full days, daytime pattern |
PerID, AbsCodeID, Start, End only |
| Part day (for example a half day) | Add a Details line with times, Days, Hours, plus WorkPatternID
|
| Night shifts or non-standard hours | Add one Details line per working day with Days and Hours, plus WorkPatternID
|
| A public holiday falls within the absence | Create two absences, one either side of the public holiday (see "Public holidays" below) |
| Past or future absence where the work pattern differs, or hours that don't fit any pattern | Use the CIPHRFLEXI work pattern (see "Absences on a different work pattern" below) |
What happens if you don't send WorkPatternID
Once Details contains at least one line, the system stops using the employee's work pattern. The absence is saved with your days and hours, but its WorkPatternID is blank. Sending an empty Details: [] behaves as if Details were not sent, so the pattern is applied as normal. To add a missing pattern afterwards, see "Adding a missing work pattern" in Step 4.
An absence without a work pattern can cause data issues if it is later edited in Ciphr HR, so always include WorkPatternID when you send Details.
Rules when you send WorkPatternID with Details
The system checks your Details lines against the work pattern. If a rule isn't met, the request fails with a 500 error and nothing is saved.
| Rule | Works | Fails |
|---|---|---|
Send one line for every working day between Start and End
|
Mon–Thu lines for a Mon–Thu pattern | Any working day left out |
| Enter shift times on the same date, as the work pattern holds them |
2027-07-26T18:30 to 2027-07-26T05:30
|
Ending on the next day (2027-07-27T05:30) or using 00:00
|
| Keep times within the working hours for that day | 09:00–12:42 on a 09:00–17:24 pattern | An evening line on a daytime pattern |
Days and Hours on each line are saved exactly as sent, and the absence totals are the sum of the lines.
Example: night shift (four 10-hour nights, Monday to Thursday)
{
"PerID": <PerID>,
"AbsCodeID": <AbsCodeID>,
"WorkPatternID": <WorkPatternID>,
"Start": "2027-07-26",
"End": "2027-07-30",
"Details": [
{ "Start": "2027-07-26T18:30:00", "End": "2027-07-26T05:30:00", "Days": 1, "Hours": 10 },
{ "Start": "2027-07-27T18:30:00", "End": "2027-07-27T05:30:00", "Days": 1, "Hours": 10 },
{ "Start": "2027-07-28T18:30:00", "End": "2027-07-28T05:30:00", "Days": 1, "Hours": 10 },
{ "Start": "2027-07-29T18:30:00", "End": "2027-07-29T05:30:00", "Days": 1, "Hours": 10 }
]
}Result: 4 days / 40 hours, linked to the employee's night shift pattern. Friday is a non-working day on this pattern, so it has no line.
Public holidays
The API does not exclude public holidays automatically. When a public holiday falls within an absence, create two absences, one either side of the public holiday, each sent with WorkPatternID. Each absence then follows the normal rules, and the public holiday is not counted.
Do not leave out WorkPatternID to get around this. The absence would save, but without a work pattern, which can cause data issues if it is later updated in Ciphr HR.
Example: night shift Monday to Thursday, with a public holiday on Tuesday 10 August
Absence 1, Monday only:
{
"PerID": <PerID>,
"AbsCodeID": <AbsCodeID>,
"WorkPatternID": <WorkPatternID>,
"Start": "2027-08-09",
"End": "2027-08-09",
"Details": [
{ "Start": "2027-08-09T18:30:00", "End": "2027-08-09T05:30:00", "Days": 1, "Hours": 10 }
]
}Absence 2, Wednesday to Friday:
{
"PerID": <PerID>,
"AbsCodeID": <AbsCodeID>,
"WorkPatternID": <WorkPatternID>,
"Start": "2027-08-11",
"End": "2027-08-13",
"Details": [
{ "Start": "2027-08-11T18:30:00", "End": "2027-08-11T05:30:00", "Days": 1, "Hours": 10 },
{ "Start": "2027-08-12T18:30:00", "End": "2027-08-12T05:30:00", "Days": 1, "Hours": 10 }
]
}Result: 3 days / 30 hours across the two absences, both linked to the employee's work pattern. For employees on a standard daytime pattern, the same split works without Details: send PerID, AbsCodeID, Start and End for each part.
Alternatively, the CIPHRFLEXI work pattern lets you keep a single absence and record the public holiday as a 0-day line (see "Alternative: the CIPHRFLEXI work pattern" below).
Absences on a different work pattern (past and future dates)
Before you create an absence for a past or future date, check which work pattern applies on that date. The API works out each day from the job history, and it accepts an outdated WorkPatternID without warning.
Checks before you POST
- Find the pattern for the absence dates from AllJobPay (Step 1), not PersonDetail. PersonDetail only shows the employee's current pattern.
-
Check whether the pattern changes during the absence. If a
DateFromfalls between the absence'sStartandEnd, create two absences, one either side of the change. - Check that the dates include working days on that pattern. An absence where every day is a non-working day fails with a 500 error.
-
If you send
Details, use theWorkPatternIDfor the absence's start date, and make the lines match that pattern's working days and shift times. -
After posting, retrieve the absence with
/WithDetailand confirm theWorkPatternID,DaysandHours.
Alternative: the CIPHRFLEXI work pattern
If you would rather not match each employee's pattern, use a CIPHRFLEXI work pattern that covers 24 hours a day, 7 days a week, and send the days and hours yourself. Because every hour of every day is available, any absence can be entered against it: past or future dates on a different pattern, night shifts, weekend work and irregular hours.
Find the 24-hour CIPHRFLEXI pattern:
GET https://<yoursystem>.ciphr.com/api/WorkPattern?Code=CIPHRFLEXI&f=ID,Code,Description,Type,AvgWeekHrs
Your system may have more than one CIPHRFLEXI pattern. Use the one that covers the full 24 hours on all 7 days: its AvgWeekHrs will be close to 168 (24 hours × 7 days). Patterns with fewer hours, or only Monday to Friday, will reject absences outside their hours. Use that pattern's ID as the WorkPatternID. If none matches, ask your system administrator to set one up.
Rules for CIPHRFLEXI
| Rule | Detail |
|---|---|
Always send Details
|
Without Details, a CIPHRFLEXI absence fails with a 500 error |
One line for every calendar day from Start to End
|
Weekends included, because every day is a working day on this pattern. A missing day fails with a 500 error |
| Days not taken: send a 0-day line | For a public holiday or day off within the range, send that day's line with "Days": 0, "Hours": 0
|
Send Days and Hours on every line |
They are saved as sent. A line with times only is saved as 0 days and 0 hours |
| Keep each line on a single date | Overnight shifts must start and end on the same date (for example 18:30 to 05:30). An end time on the next day fails with a 500 error |
Example: three days with a public holiday on the Tuesday
{
"PerID": <PerID>,
"AbsCodeID": <AbsCodeID>,
"WorkPatternID": <CIPHRFLEXI WorkPatternID>,
"Start": "2027-05-03",
"End": "2027-05-05",
"Details": [
{ "Start": "2027-05-03T09:00:00", "End": "2027-05-03T17:00:00", "Days": 1, "Hours": 7.5 },
{ "Start": "2027-05-04T09:00:00", "End": "2027-05-04T09:00:00", "Days": 0, "Hours": 0 },
{ "Start": "2027-05-05T09:00:00", "End": "2027-05-05T17:00:00", "Days": 1, "Hours": 7.5 }
]
}Result: 2 days / 15 hours, linked to the 24-hour CIPHRFLEXI pattern. With CIPHRFLEXI, a public holiday can be handled in a single absence rather than two.
Step 4: Update an absence (PUT)
A PUT replaces the whole absence record, so always send the complete record, including the Details array, to PUT /api/Absence/{id}. Sending only the fields you want to change will fail. A successful update returns 200 OK.
Minimum PUT body: PerID, AbsCodeID, Start, End and a Details array with one line for each working day between Start and End.
Keep the absence's WorkPatternID in the body. When it is present, the Details rules in Step 3 apply: one line per working day, with shift times as the work pattern holds them.
Recommended method
- Retrieve the current record, including its detail lines:
GET /api/Absence/{id}/WithDetail - Change the values you need in the returned JSON, keeping every other field and each detail line's
ID. - If you change the dates, add or remove
Detailslines so there is one for each working day. - Send the whole edited record back:
PUT /api/Absence/{id} - Check the result:
GET /api/Absence/{id}/WithDetailand confirm theDaysandHourstotals.
Example: extend a holiday by a half day and add a note
An absence runs Monday 10 to Wednesday 12 May (3 days). To extend it to a half day on Thursday 13 May, add a new Details line and update End and Notes:
{
"PerID": <PerID>,
"AbsCodeID": <AbsCodeID>,
"Start": "2027-05-10",
"End": "2027-05-13",
"Notes": "Updated via API",
"Details": [
{ "ID": <DetailID 1>, "Start": "2027-05-10T09:00:00", "End": "2027-05-10T17:24:00", "Days": 1, "Hours": 7.4 },
{ "ID": <DetailID 2>, "Start": "2027-05-11T09:00:00", "End": "2027-05-11T17:24:00", "Days": 1, "Hours": 7.4 },
{ "ID": <DetailID 3>, "Start": "2027-05-12T09:00:00", "End": "2027-05-12T17:24:00", "Days": 1, "Hours": 7.4 },
{ "Start": "2027-05-13T09:00:00", "End": "2027-05-13T12:42:00", "Days": 0.5, "Hours": 3.7 }
]
}(The other fields returned by the GET, such as ID, EmployeeNumber and WorkPatternID, are left out here for readability. Include them as returned.)
Important: a new detail line added through PUT is saved with 0 days and 0 hours, even if you send Days and Hours. To set them, repeat steps 1–4: the new line now has an ID, and its Days and Hours are then saved. In this example the totals go from 3 days / 22.2 hours to 3.5 days / 25.9 hours. |
Adding a missing work pattern
If an absence was created with Details but no WorkPatternID, you can add the pattern with a PUT:
GET /api/Absence/{id}/WithDetail- Set
WorkPatternIDto the employee's work pattern ID, leaving everything else unchanged. -
PUT /api/Absence/{id}with the whole record.
This works when the absence has a line for every working day. It fails with a 500 error if a working day was left out. In that case, delete the absence and recreate it as two absences either side of the missing day. Overnight lines that end on the next day are accepted on a PUT.
Error messages and what they mean
Several messages point to something other than their wording suggests, so check this table before changing your key's permissions.
| Code | Message | Likely cause | What to do |
|---|---|---|---|
| 400 | No security filtering found |
The API key has no data security filter | Add a security filter to the key in API Configuration |
| 400 | Unauthorized access to API end node for ... method |
The access needed (Create, Read or Update) isn't switched on for that entity or property | Enable it in API Configuration |
| 400 | Unable to retrieve the data |
AbsenceDetail isn't enabled on the key. On a POST, the absence has still been saved | Enable AbsenceDetail; check for the record before retrying |
| 400 | Access denied to this person |
PerID is missing or invalid, the employee is outside the key's security filter, or EmployeeNumber was sent instead of PerID
|
Send a valid PerID; check the security filter |
| 400 | Access denied to this Absence Type |
AbsCodeID is missing, the code isn't enabled for the key, or AbsenceCode was sent instead of AbsCodeID. On PUT, this also appears when only some fields are sent |
Send a valid AbsCodeID; check absence type access; on PUT, send the full record |
| 400 | Unable to execute direct SQL (SqlDateTime overflow...) |
Start or End is missing |
Send both dates |
| 500 | We have experienced a problem... |
Dates: End before Start, or every day in the range is a non-working day |
Check the dates |
| 500 | We have experienced a problem... |
Work pattern rules (Step 3): a working day has no line, shift times end on the next day, or times fall outside working hours | Send one line per working day with times as the pattern holds them |
| 500 | We have experienced a problem... |
PUT without Details, or with Details that don't match the date range |
Send the full record from WithDetail
|
Good practice
- Always send WorkPatternID with Details. Otherwise the absence is saved without a work pattern, which can cause data issues if it is later edited in Ciphr HR.
- Split absences around public holidays. Create one absence before the public holiday and one after it, rather than leaving the day out of a single absence.
- Check the work pattern for past and future dates. Use AllJobPay to find the pattern that applies on the absence dates, or use CIPHRFLEXI and send each day's hours.
-
Check for existing absences first. The API does not stop you creating an absence that overlaps an existing one. Use
GET /api/Absence?PerID=<PerID>&Start=[ge]2027-05-01to check before you POST. -
Never retry blindly after an error. Some errors (such as
Unable to retrieve the data) are returned after the absence has been saved. Check for the record before sending again. -
Store the returned ID. The
IDin the 201 response is what you need for any later PUT or DELETE. -
Verify after every write. Retrieve the record with
/WithDetailand confirm theDays,HoursandWorkPatternIDare what you expect. - Check employees on shift or non-standard patterns. Compare an API-created absence with the same absence entered in Ciphr HR before running a bulk import.
- Test with a small set first. Create one or two absences and review them in Ciphr HR before importing in bulk.
-
Deleting an absence:
DELETE /api/Absence/{id}returns 204 No Content on success, if DELETE is enabled for your key.
Footer
Comments
0 comments
Please sign in to leave a comment.