This document walks you through how to use our AI to solve your scheduling problems.
If you have any questions just reach out to us at [email protected].
Did you see our Swagger Documentation? You can check it out at https://planmyshifts.com/swagger/partner/v1. On one hand, we have a simple API with only two endpoints. On the other the hand, the payload is complex so we strongly recommend that you scaffold your API client on the OpenAPI file in the Swagger documentation.
A simple schedule with 2 shifts and 2 employees has 9 possible solutions so any real-life schedule becomes too complex to brute-force.
With our API you can simply POST
your problem to us and we will return the optimal schedule given your rules. We will elaborate below on the details but in the big picture: Our data structure is highly abstracted so it can fit any scheduling problem.
You might think that consuming a scheduling API is a GDPR risk but as you will see this API works on IDs.
According to GDPR, identifiers are only personally identifiable to a third party like us if we can reasonably identify an individual based on the data so make sure to use random identifiers that you map to internal identifiers on your side.
We use Bearer authentication (also called token authentication) so get your token by doing:
curl -H "Accept: application/json" \
-H "Content-type: application/json" \
-X POST --data-binary '{"email":"[email protected]","password":"YOUR_PASSWORD"}' \
https://planmyshifts.com/api/auth
This way we issue API tokens without the complication of OAuth. This feature is inspired by GitHub and other applications which issue "personal access tokens".
Put this YOUR_TOKEN
in the header of your request:
curl -H "Accept: application/json" \
-H "Content-type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-X POST --data-binary '{JSON_PAYLOAD}' \
https://planmyshifts.com/api/v1
Solving a schedule is computational intensive and takes up to 10 minutes to solve so we use callbacks to POST
the answer to you.
You specify the endpoint and the credentials we should use to authenticate in the payload. We require you to use authentication variables for our callback to you for your safety.
{
// The rest of the payload goes here
...
"callbackUrl": "https://your_application.com/CALLBACK_ENDPOINT",
"callbackAuthenticationHeaderName": "YOUR_AUTH_HEADER_NAME",
"callbackAuthenticationHeaderValue": "YOUR_OWN_TOKEN",
}
We will retry 3 times with 500 miliseconds between attempts before stopping. On the last failure we will send you an email with the last error that your server returned.
In this paragraph we will put further words onto the request payload.
For an elaboration you can always email us at [email protected] or look at the Swagger Document above.
Let us start with a valid payload along with some inline comments:
{
"employees": [ # The list of users who takes shifts for the duraton of the schedule
{
"id": 1, # This has to be a unique integer for every user
"teams": [
{
"id": 1, # This has to be a unique integer for every team should be found the teams in the "shifts" part of the payload.
"start": "2020-06-28T09:00:00+00:00", # In case the employee is only assigned to this team for a limited period, otherwise null
"end": "2025-06-28T09:00:00+00:00" # In case the employee is only assigned to this team for a limited period, otherwise null
}
],
"skills": [
{
"id": 1, # This has to be a unique integer for every skill should be found the skill in the "shifts" part of the payload.
"start": "2020-06-28T09:00:00+00:00", # In case the employee has this skill for a limited period, otherwise null
"end": "2025-06-28T09:00:00+00:00" # In case the employee has this skill for a limited period, otherwise null
}
],
"contract": {
"hoursBetweenShift": 11, # If not relevant set to a LOW number such as 0.
"maxHoursDay": 12, # If not relevant set to a HIGH number such as 999.
"maxHoursWeek": 50, # If not relevant set to a HIGH number such as 999.
"maxHoursMonth": 190, # If not relevant set to a HIGH number such as 999.
"maxWorkDaysMonth": 20, # If not relevant set to a HIGH number such as 999.
"maxConsecutiveDays": 4, # If not relevant set to a HIGH number such as 999.
"minConsecutiveDays": 2, # If not relevant set to a LOW number such as 0.
"weekendWorkFrequency": 4, # How often do you work weekends. If not relevant set to a LOW number, ie. 1
"maxNhours": 144, # This one goes together with InKWeeks, so in this configuration the employee will never work more than 144 hours over a 4 week period.
"InKWeeks": 4, # In most labour-unionized contracts you deduct parts of the maxNhours when there are vacations or national holidays.
"maxConsecutiveLateShifts": 3, # If not relevant set to a HIGH number such as 999.
"maxConsecutiveNightShifts": 4 # If not relevant set to a HIGH number such as 999.
},
"wishes": [ # The times periods where the employee cannot (or wants to) work
{
"type": "UNAVAILABLE", # UNAVAILABLE = cannot work, UNDESIRED = would rather not work and DESIRED = wants to work.
"start": "2020-06-28T09:00:00+00:00",
"end": "2025-06-28T09:00:00+00:00"
}
]
}
],
"shifts": [
{
"id": 1, # This has to be a unique integer for every user.
"spot": 1, # This has to be in the "spots" part of the payload.
"start": "2020-06-28T09:00:00+00:00", # The time the shift starts.
"end": "2020-06-28T16:00:00+00:00", # The time the shift end.
"importance": "MANDATORY", # the importance of assigning this shift. Can be MANDATORY and OPTIONAL.
"suggestedEmployees": [ # If you prefer certain employees to take the shift, suggest it to the AI here.
1
],
"fixed": true, # If fixed, you must assign an employee and we wont change that employee.
"historic": false # If historic, we will use this shift to seed the solver and won't change it. Use this flag if past shifts are important for contractual terms such as consecutive days of work.
"employee": 1 # If fixed is false, set this to null and we will assign an employee.
"foreignInt": 5 # To be able to assist you in the best way of potential issues/questions, this can help you and us the best way to track down the issues/questions. This field is OPTIONAL, if filled, make it a unique integer.
}
],
"spots": [
{
"id": 1, # This has to be a unique integer for every user.
"teams": [ # This has to be a unique integer for every team should be found the teams in the "employees.*.teams" part of the payload.
1
],
"skills": [ # This has to be a unique integer for every skill should be found the skills in the "employees.*.skills" part of the payload.
1
],
"foreignInt": 10 # To be able to assist you in the best way of potential issues/questions, this can help you and us the best way to track down the issues/questions. This field is OPTIONAL, if filled, make it a unique integer.
}
],
"settings": {}, # Unless you have worked with this API before just leave "settings" as an empty object, i.e {} and we will add the correct values. Otherwise, see the possible keys and values in the Swagger-documentation.
"callbackUrl": "https://www.yourdomain.app/custom_callback_url",
"callbackAuthenticationHeaderName": "X-Api-Key",
"callbackAuthenticationHeaderValue": "SECRET"
}
In the part below we review the enums of this payload. We have done our best to keep them to a minimum but consider that any schedule is a balance of priorities: From the super-important contractual priorities all the way to the "nice-to" priorities such as avoiding certain shifts for employee X.
You can flag your shifts with either "importance": "MANDATORY"
or "importance": "OPTIONAL"
depending on how
important the shift is to you.
If you are under-staffed you can prioritise which shifts gets staffed before others.
You can flag your employees' wishes with either "type": "UNAVAILABLE"
, "type": "UNDESIRED"
or "type": "DESIRED"
.
Use the first if you have promised an employee vacations, paternity, etc. Use the second if the employee would rather not work but can be asked to. Use the third if the employee would prefer working on a given day.
The settings object is the most complicated object, which is why it is optional. We use a default configuration that works for "most" workplaces. If you really need to tweak the settings, we strongly recommend that you do it together with us.
Please also have a look our [setting-generator](/setting generator) before filling out the settings object.
Every setting has four degrees, so you can prioritise them amongst each other. Most workplaces will never break a contractual obligation at the expense of a staffing shift. But some workplaces actually do the opposite: They would rather break the contractual obligation to make sure shifts are staffed.
If, for example, you are such a workplace then configure the settings
object like so:
{
...
"settings": {
"assignShift": { "firstPriorityWeight": 3, "secondPriorityWeight": 0, "thirdPriorityWeight": 0, "fourthPriorityWeight": 0 },
"maxNHoursInKWeeks": { "firstPriorityWeight": 0, "secondPriorityWeight": 2, "thirdPriorityWeight": 0, "fourthPriorityWeight": 0 },
}
}
With these settings, we will always assign shifts at the expense of respecting the maxNHoursInKWeeks
in the employees' contracts. You may note that we have set the numeric values to 3 and 2, respectively. Our API will return a HTTP 422 and a validation error message if the numbers are not integers between 1 and 5.
In case you are operating something like a multi-tenant app, you might prefer that each tenant has its own credentials to authenticate against our endpoints. We call them consumers.
With your credentials, you can create and delete consumers as described in our Swagger documentation. Here is an example of how to create a consumer.
curl -H "Accept: application/json" \
-H "Content-type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-X POST --data-binary '{ "email": "[email protected]", "password": "AT_LEAST_10_CHARS_LONG" }' \
https://planmyshifts.com/api/v1/consumers
Note that this consumer cannot create other consumers. Only your original consumer can do that.
On top of that, it is also only possible for you original consumer to DELETE
a consumer, if that given consumer no
longer is of use to you.
Deleting is done by the DELETE
endpoint as described in our Swagger documentation.
Our API will return a 401
(Unauthorized) or 404
(Not Found) if unauthorized attempts to either of the endpoints are
made. Likewise, our API will return 201
(Created) for a successful POST
and 204
(No Content) for a
successful DELETE
. Lastly, our API will return 422
(Unprocessable Entity) if any does not pass validation.