| name | onboard_employee_offer_letter |
| tier | 3 |
| task | T21 |
| description | This skill onboards a new employee from a PDF offer letter (tilbudsbrev). The offer letter has FEWER fields than an employment contract — NO personnummer, NO email, NO bank account, NO STYRK code, NO salary type. It DOES have working hours (arbeidstid). Keywords include "offer letter", "tilbudsbrev", "tilbodsbrev", "carta de oferta", "lettre d'offre", "Angebotsschreiben", "onboarding", "incorporación", "standard working hours", "arbeidstid", "standard arbeidstid", "horas de trabajo estándar", "configure standard working hours", "konfigurer standard arbeidstid". |
Onboard Employee from Offer Letter
6-7 API calls.
Step 1: Read the PDF and find these fields
| # | Look for | Example value |
|---|
| 1 | Name (from greeting "Kjære X Y" or position line) | Alice Taylor |
| 2 | Date of birth (fødselsdato) | 01.11.1994 |
| 3 | Department (avdeling, from intro text) | Salg |
| 4 | Job title (from intro, e.g. "stillingen som X") | Regnskapssjef |
| 5 | Employment form (ansettelsesform) | Fast stilling |
| 6 | Percentage (stillingsprosent) | 80.0 |
| 7 | Annual salary (årslønn) | 790000 |
| 8 | Start date (tiltredelse) | 10.08.2026 |
| 9 | Working hours per day (arbeidstid) | 6.0 |
Convert dates DD.MM.YYYY → YYYY-MM-DD. Split name into firstName + lastName.
NOTE: This PDF does NOT have personnummer, email, bank account, STYRK code, or salary type. Do NOT search for them or make them up.
Call 1: GET /department + GET /salary/settings (parallel)
GET /department?fields=id,name&count=20
GET /salary/settings
Call 2: POST /department (if needed) + GET /employee/employment/occupationCode (parallel)
If the PDF's department is not in Call 1 results:
POST /department
{"name": "<field 3>"}
Search by job title (there is NO STYRK code in offer letters):
GET /employee/employment/occupationCode?nameNO=<field 4>&count=10&fields=id,code,nameNO
Pick the result whose nameNO best matches the job title.
Call 3: POST /employee (basic — WITHOUT employmentDetails)
Create the employee with employment but WITHOUT nested employmentDetails:
POST /employee
{
"firstName": "<first name>",
"lastName": "<last name>",
"dateOfBirth": "<field 2 as YYYY-MM-DD>",
"userType": "NO_ACCESS",
"department": {"id": "<from Call 1 or 2>"},
"employments": [{
"startDate": "<field 8 as YYYY-MM-DD>",
"isMainEmployer": true,
"taxDeductionCode": "loennFraHovedarbeidsgiver"
}]
}
From the response, extract the employment ID: response.value.employments[0].id.
Call 4: POST /employee/employment/details (separate — ensures persistence)
POST /employee/employment/details
{
"employment": {"id": <employment_id_from_Call_3>},
"date": "<field 8 as YYYY-MM-DD>",
"employmentType": "ORDINARY",
"employmentForm": "PERMANENT",
"remunerationType": "MONTHLY_WAGE",
"occupationCode": {"id": "<from Call 2>"},
"percentageOfFullTimeEquivalent": <field 6>,
"annualSalary": <field 7>,
"workingHoursScheme": "NOT_SHIFT",
"payrollTaxMunicipalityId": {"id": "<municipality_id_from_Call_1>"}
}
Call 5: POST /employee/standardTime
POST /employee/standardTime
{
"employee": {"id": "<employee_id_from_Call_3>"},
"fromDate": "<field 8 as YYYY-MM-DD>",
"hoursPerDay": <field 9>
}
6-7 calls, 0 errors.