For the complete documentation index, see llms.txt. This page is also available as Markdown.

Upsert Employee

Looking for a step-by-step walkthrough of a specific job — create in Flaik only, create in the POS only, archive someone, make an instructor inactive in RTP? See Common Scenarios. This page is the field-by-field contract reference.

Overview

Creates or updates an employee in Flaik, and optionally propagates the same person data into the resort's POS as a POS Person, POS Employee, and/or POS Instructor record. A single request can target any combination of those four destinations using the actionUpsert* flags described below.

Processing is asynchronous and queued. The submit call returns a upsertEmployeeRequestId that you then poll via the Upsert Status endpoint until the request reaches a terminal state (CompletedSuccessfully or CompletedWithError). Optionally, supply a webHookUrl and Flaik will POST the result back to you when processing finishes.

1. POST /api/employee/upsert { … }                  → 200 { upsertEmployeeRequestId, addedToProcessingQueueUtc }
2. GET  /api/employee/upsert/{upsertEmployeeRequestId}  → poll until processingStatus is 3 or 4
3. (optional) Flaik POSTs the result to webHookUrl when processing completes

Authentication

Include your access token in the Authorization header:

Authorization: Bearer {access_token}

This endpoint requires the flaik.connect.api.write scope. (Tokens carrying the legacy LegacyEnterpriseApiAdmin scope are also accepted for backwards compatibility, but new integrations should use flaik.connect.api.write.) See the Authentication Guide for details.

Submit Endpoint

POST {api-url}/api/employee/upsert
Content-Type: application/json

What gets updated — the four action flags

The four actionUpsert* flags decide which downstream systems get touched. At least one must be true — a request with all four false is rejected with a 400.

Flag

When true

actionUpsertInFlaik

Creates or updates the Flaik employee record. Requires email, and requires primaryTeachingDisciplineId when creating (no id supplied).

actionUpsertPosPerson

Creates or updates the POS Person profile. Requires the upsertPosPerson block on the request. Uses top-level fields for everything except posGenderCode.

actionUpsertPosEmployee

Creates or updates the POS Employee record. Requires the upsertPosEmployee block.

actionUpsertPosInstructor

Creates or updates the POS Instructor profile. Requires the upsertPosInstructor block.

POS-side actions only have an effect for resorts with a POS integration enabled and the relevant subsystem configured.

Example Request — minimal Flaik-only upsert

The upsertEmployment block above is shown complete, which is a requirement rather than a style choice — see The employment block is all-or-nothing. The three wage position ids are sent as "" here because this employee has none; leaving the keys out is rejected.

Example Request — Flaik + POS Person + POS Instructor

Example Response (submit)

Field
Type
Description

upsertEmployeeRequestId

integer

Identifier for this upsert request — pass to the Upsert Status endpoint to poll for the result

addedToProcessingQueueUtc

datetime

UTC timestamp the request was queued (YYYY-MM-DDTHH:mm:ss)

Validation Rules

The submit endpoint enforces the following rules. A 400/ProblemDetails is returned with the violated rule's message:

Rule
Message

firstName is required

First Name is Required.

lastName is required

Last Name is Required.

dateOfBirth is required

Date Of Birth is Required.

email is required when actionUpsertInFlaik = true

Email required when ActionUpsertInFlaik set to true.

upsertPosPerson block is required when actionUpsertPosPerson = true

UpsertPosPerson required when ActionUpsertPosPerson set to true.

upsertPosEmployee block is required when actionUpsertPosEmployee = true

UpsertPosEmployee required when ActionUpsertPosEmployee set to true.

upsertPosInstructor block is required when actionUpsertPosInstructor = true

UpsertPosInstructor required when ActionUpsertPosInstructor set to true.

At least one actionUpsert* flag must be true

Nothing to process - at least one ActionUpsert* flag must be true.

upsertPosEmployee.posEmployeeId is required when actionUpsertPosEmployee = true

PosEmployeeId is required when ActionUpsertPosEmployee is set to true.

primaryTeachingDisciplineId is required when creating a Flaik employee (actionUpsertInFlaik = true and no id)

PrimaryTeachingDisciplineId is required when creating a flaik employee.

primaryTeachingDisciplineId, when supplied, must be between 1 and 65535

PrimaryTeachingDisciplineId must be between 1 and 65535.

status, when supplied, must be a valid employee status

Status must be a valid employee status (1=Active, 2=Archived, 3=Candidate).

upsertEmployment.startDate is required when upsertEmployment is supplied

StartDate is required when upsertEmployment is supplied - the employment block is full-replace, so every field must be sent.

upsertEmployment.jobTitleId is required when upsertEmployment is supplied

JobTitleId is required when upsertEmployment is supplied - the employment block is full-replace, so every field must be sent.

upsertEmployment.employmentTypeId is required when upsertEmployment is supplied

EmploymentTypeId is required when upsertEmployment is supplied - the employment block is full-replace, so every field must be sent.

upsertEmployment.wageTeachingPositionId is required when upsertEmployment is supplied

WageTeachingPositionId is required when upsertEmployment is supplied - send "" if the employee has none. Omitting it would clear the stored value.

upsertEmployment.wageNonTeachingPositionId is required when upsertEmployment is supplied

WageNonTeachingPositionId is required when upsertEmployment is supplied - send "" if the employee has none. Omitting it would clear the stored value.

upsertEmployment.wageTrainingPositionId is required when upsertEmployment is supplied

WageTrainingPositionId is required when upsertEmployment is supplied - send "" if the employee has none. Omitting it would clear the stored value.

Not validated here, but still enforced downstream: an RTP instructor must have a supervisor. That rule is POS-specific, so it is not checked when you submit — a missing supervisor surfaces as a per-destination error on the polling response (actionUpsertPOSInstructorErrorMessage) rather than as a 400.

Request Body Field Reference

Identifiers

Field
Type
Required
Description

id

integer

No

Flaik employee identifier. Omit for new employees; include to update an existing one.

posIdentifier

string

No

POS person identifier (the resort's POS unique person id)

payrollIdentifier

string

No

Employee identifier in your payroll system

payrollSecondaryIdentifier

string

No

Secondary payroll identifier (e.g. a tax ID), if relevant

Person information

Field
Type
Required
Description

status

integer

No

1 Active Staff, 2 Archived (removed from staff management), 3 Candidate (talent pool). Defaults to Active on create. Send 2 to archive — see Common Scenarios.

firstName

string

Yes

Legal first name

middleName

string

No

Middle name. Forwarded to RTP if RTP integration is enabled. Not stored in Flaik directly.

lastName

string

Yes

Legal last name

preferredName

string

No

Preferred display name

dateOfBirth

date

Yes

YYYY-MM-DD

isSupervisor

boolean

No

Whether this employee is a supervisor. This is not who they report to.

supervisorFlaikEmployeeId

integer

No

Flaik employee id (employees.id) of this employee's supervisor. A Flaik identifier, not a POS or payroll id. The POS instructor's supervisor is a separate field — see upsertPosInstructor.posInstructorSupervisorUniqueIdentifier below.

genderName

string

No

Free-text gender identifier

genderPronoun

string

No

Preferred pronouns (e.g. "he/him")

Contact and address

Field
Type
Required
Description

email

string

when actionUpsertInFlaik

Primary email

phoneNumber

string

No

Primary phone

addressCountry

string

No

Country (free text)

addressState

string

No

State / province

addressStreetNumberAndName

string

No

Street line

addressCity

string

No

City

addressZip

string

No

ZIP / postal code

Passes

Field
Type
Required
Description

liftPassId

string

No

Lift pass identifier

mediaPassId

string

No

Media pass identifier

rfidPassId

string

No

RFID pass identifier

Teaching profile

Field
Type
Required
Description

primaryTeachingDisciplineId

integer

Yes, when creating a Flaik employee

The employee's primary teaching discipline, mirroring the Flaik UI. Non-teaching staff are not exempt — pass the Admin discipline id. Must be between 1 and 65535. Not required on update, so a partial update need not resend it. Flaik Connect does not currently expose a discipline reference list — obtain the valid ids for your resort (including the Admin discipline id) from your Flaik implementation contact.

teachingDisciplineIds

integer[]

No

Discipline IDs the instructor can teach

maxAbilityLevelIdAlpine

integer

No

Highest alpine level taught

maxAbilityLevelIdSnowboard

integer

No

Highest snowboard level taught

maxAbilityLevelIdTelemark

integer

No

Highest telemark level taught

maxAbilityLevelIdNordic

integer

No

Highest nordic level taught

maxAbilityLevelIdAdaptive

integer

No

Highest adaptive level taught

maxAbilityLevelIdAdaptiveSnowboard

integer

No

Highest adaptive snowboard level taught

Action flags

Field
Type
Required
Description

actionUpsertInFlaik

boolean

No (default false)

Upsert the Flaik employee record

actionUpsertPosPerson

boolean

No (default false)

Upsert the POS Person profile

actionUpsertPosEmployee

boolean

No (default false)

Upsert the POS Employee record

actionUpsertPosInstructor

boolean

No (default false)

Upsert the POS Instructor profile

Webhook

Field
Type
Required
Description

webHookUrl

string

No

If supplied, Flaik will POST the processing result to this URL when the request reaches a terminal state

upsertEmployment (optional block — but all-or-nothing when you send it)

Omitting this block entirely is fine and safe: the employee record is still created or updated, no error is raised, and the existing employment record is left completely untouched. If you are not changing someone's job title, dates or wages, leave it out.

What you must not do is send a partial block.

The employment block is all-or-nothing

Unlike the top-level employee fields, this block is full-replace: the employment record is rebuilt from exactly what you send, and anything you leave out is written as empty rather than carried over. To stop that happening silently, the API rejects an incomplete block with a 400.

Whenever upsertEmployment is present, all six of these are required:

startDate · jobTitleId · employmentTypeId · wageTeachingPositionId · wageNonTeachingPositionId · wageTrainingPositionId

Send "" for any wage position id the employee does not have — an empty string is a valid, explicit "none". It is the missing key that is rejected, not the empty value.

endDate is the exception, and it is not validated. It is optional by design, because employment can be open-ended. That means the API cannot tell "I left endDate out" apart from "this employee has no end date" — so an omitted endDate clears any stored end date, with no error. If the employee has an end date you want to keep, send it on every request that includes this block.

The four wage rates are also not validated, because they are plain numbers and an omitted rate is indistinguishable from a genuine 0.0. Omitting teachingWageHourly will set that wage to zero. Always send all four.

In short: treat upsertEmployment as a complete statement of the employee's employment, every time you send it. Partial blocks are either rejected or destructive.

Field
Type
Required
Description

jobTitleId

integer

Yes

Flaik job title identifier

employmentTypeId

integer

Yes

Employment type identifier

startDate

datetime

Yes

Employment start

endDate

datetime

No — but see below

Employment end. Nullable by design, so it is not validated — and omitting it clears any stored end date. Send it whenever the employee has one

teachingWageHourly

decimal

Not validated

Hourly wage for teaching activities. Omitting it sets the wage to 0.0

nonTeachingWageHourly

decimal

Not validated

Hourly wage for non-teaching activities. Omitting it sets the wage to 0.0

trainingWageHourly

decimal

Not validated

Hourly wage for training activities. Omitting it sets the wage to 0.0

privateRequestWageHourly

decimal

Not validated

Hourly wage for private-request activities. Omitting it sets the wage to 0.0

wageTeachingPositionId

string

Yes

External wage position identifier — teaching. Send "" if none

wageNonTeachingPositionId

string

Yes

External wage position identifier — non-teaching. Send "" if none

wageTrainingPositionId

string

Yes

External wage position identifier — training. Send "" if none

"Required" here means required when the block is present. The block itself remains optional — see above.

upsertPosPerson (required when actionUpsertPosPerson = true)

Field
Type
Description

posGenderCode

string

POS-specific gender code. Override; everything else is taken from the top-level fields on the request.

Status vs Type — read this before setting any POS code

Four of the POS fields below pair up into two easily-confused sets. Sending a value to the wrong one does not error; it silently changes the wrong attribute. Getting this backwards has previously reclassified people's employment type while trying to deactivate them.

Concern

POS Employee

POS Instructor

Lifecycle — is this record active?

posEmployeeStatusCode

posProfileStatus

Type — what kind of employment?

posEmployeeTypeCode

posInstructorEmployeeTypeCode

For RTP, the lifecycle codes are 1 Active, 2 Inactive, 3 Changed, 4 Deleted, and the instructor employment types are 1 Full Time, 2 Part Time, 3 Casual. RTP names the underlying employment-type table "InstructorStatus", which is why posInstructorEmployeeTypeCode sounds like a status but is not one. Always source valid codes for your resort from GET api/ResortConfiguration/* rather than hard-coding them — the instructor employment types come from InstructorEmploymentTypes (field resortInstructorStatusCode).

upsertPosEmployee (required when actionUpsertPosEmployee = true)

Field
Type
Description

posEmployeeId

string

Required when actionUpsertPosEmployee is true. The employee's payroll/HR identifier in the POS. A blank value is rejected with a 400.

posGovernmentId

string

Government ID stored in the POS

posEmployeeTypeCode

string

POS employee type code (not lifecycle)

posEmployeeDepartmentCode

string

POS department code

posEmployeeResortCode

string

POS resort code

posEmployeeStatusCode

string

POS employee lifecycle status code. RTP: 1 Active, 2 Inactive. Send 2 to make the POS employee inactive.

upsertPosInstructor (required when actionUpsertPosInstructor = true)

Prerequisite: POS Integration API 2.6.0 or later. Every field in this block behaves differently below 2.6.0 — employment type cannot be set on create, updates that omit it are rejected, and omitting rank or lesson location on a partial update silently overwrites them. The POS Integration API is installed per resort and does not update with Flaik Connect; check yours with GET /health. See Common Scenarios › Prerequisites for the full table and the pre-2.6.0 workaround.

Field
Type
Description

posInstructorSupervisorUniqueIdentifier

string

POS supervisor identifier. If omitted but supervisorEmployeeId is set on the top level, Flaik will fall back to that supervisor's POS unique identifier.

posInstructorLocationCode

string

Home location code

posInstructorLessonLocationCode

string

Default lesson location code

posInstructorPriorityRanking

string

Priority ranking for assignment

posInstructorEmployeeTypeCode

string

The instructor's employment type in the POS — RTP: 1 Full Time, 2 Part Time, 3 Casual. Valid values come from GET api/ResortConfiguration/InstructorEmploymentTypes (field resortInstructorStatusCode). Not a lifecycle status — see the Status vs Type note above. Omit on create and RTP defaults the instructor to Full Time. Omitting it on an update preserves the existing value (2.6.0+; see the prerequisite above).

posInstructorDisciplines

object

Map of discipline code → max ability level code. Overrides teachingDisciplineIds/maxAbilityLevelId* from the top level.

posProfileStatus

string

The instructor profile's lifecycle status — RTP: 1 Active, 2 Inactive, 3 Changed, 4 Deleted. Send 2 to make the instructor inactive. Numeric code, not a name: "Active" is not a valid value.

Webhook Callback

If webHookUrl is supplied on the request, Flaik will POST the same body returned by the Upsert Status endpoint to that URL once processing reaches CompletedSuccessfully or CompletedWithError. Webhook delivery is best-effort — for guaranteed observation of the result, also poll the status endpoint.

Error Responses

HTTP Status
Description

400

One of the validation rules above was violated

400

Request body is malformed JSON

401

Missing or invalid access token

403

Token does not include the flaik.connect.api.write scope

Need help with employee data ingest? Contact resortsupport@flaik.com.

Last updated

Was this helpful?