GreenHouse to Workday
Connected Systems
- Greenhouse (Source)
- Workato (Middleware)
- Platypus (Middleware)
- Slack (Notifications)
- Denomas (Error Handling)
- Workday (Destination)
Summary
Sync newly hired employees from Greenhouse to Workday, taking care of any data mapping that needs to happen and sending out slack notifiactions on success or error.
For in depth information on this integration please view the workato documentation and the greenhouse controller documentation.
Sequence
sequenceDiagram
participant Greenhouse
participant Workato
participant Platypus
participant Workday
participant Slack
participant Denomas
Greenhouse ->> Workato :webhook
Workato ->> Platypus :POST /greenhouse/new-hire
critical Hire the Employee
Platypus ->> Workday :Get Position Data
Workday -->> Platypus :Workday Position Data
Platypus ->> Workday :Get Current Greenhouse Workers
Workday -->> Platypus :Current Workers in Greenhouse Report
alt Worker Exists in Report
alt Worker Is Not Active or a Future Hire
Platypus ->> Workday :Get applicants
Workday -->> Platypus :Applicant found by Id
end
Platypus ->> Workday :Get Greenhosue Pre Hire
Workday -->> Platypus :Pre Hire found By Id
end
alt Not a Pre Hire
Platypus ->> Workday :Get Former Workers
Workday -->> Platypus :Former Worker Data
end
Platypus ->> Workday :Put Applicant Into System
Workday -->> Platypus :Applicant Data and ID
Platypus ->> Workday :Hire Employee
Workday -->> Platypus :New Employee Id
Platypus ->> Slack :Notification of a Successful Hire
option An exception happens from Workday
Platypus ->> Slack :Error Notification that something went wrong
Platypus ->> Denomas :Create incident of a failure
end
Error Handling
When an exception happens during the sync hiring process, a notification is
sent to Slack on the #peopleops-alerts-private channel and to Denomas at the
automated incidents project.
The following errors can be thrown inside the process and are handled accordingly
InternalApplicantError: thrown when the application’s source is ‘Internal Applicant’ meaning they should be updated manuallyNotFoundPositionError: thrown when the position ID we were given does not exist. The position ID is the same asP-candidate.idExistingWorkerError: thrown when we attempt to hire a worker that alrady exists, which is either current or a future hire.FormerWorkerError: thrown when a new hire is a former worker from before January 1 2022, tracked by candidate id and emailIncompleteHireError: thrown when Workday returns a value that does not have an employee ID
If any of the above errors are thrown, manual intervention is necessary inside workday to help ease the process along.
If an error that is not an instance of the above errors is thrown, the message to Slack mentions that an Integrations Engineer should look at the logs on both sides of the process.
Data Model
erDiagram "Greenhouse Employee" ||--|| "Workday Employee" : "Maps to"
The below fields are written by their API names in dot notation.
| Greenhouse Field | Workday Field |
|---|---|
| opening.finance_id.value | Hire_Employee_Data.Edit_Custom_IDs_Sub_Proccess.Custom_Identification_Data[].Custom_ID[].Custom_ID_Data.ID |
| opening.type.value | Hire_Employee_Data.Hire_Employee_Event_Data.Employee_Type_Reference |
| credited_to.email | Applicant_Data.Recruiting_Data.Applicant_Comments |
| credited_to.employee_id | Applicant_Data.Recruiting_Data.Referred_by_Worker_Reference[].ID[].$value |
| credited_to.employee_id | Applicant_Data.Recruiting_Data.Applicant_Comments |
| credited_to.name | Applicant_Data.Recruiting_Data.Applicant_Comments |
| candiate.id | Applicant_Data.Applicant_ID |
| candiate.id | Request_Reference.Position_Reference[].ID[].$value |
| candidate.first_name | Applicant_Data.Personal_Data.Legal_Name_Data.Name_Detail_Data.First_Name |
| candidate.last_name | Applicant_Data.Personal_Data.Legal_Name_Data.Name_Detail_Data.Last_Name |
| candidate.email_addresses[].value | Applicant_Data.Personal_Data.Contact_Data.Email_Address_Data |
| candidate.attachments[].filename | Worker_Document_Data.Filename |
| candidate.nickname.value | Applicant_Data.Personal_Data.Name_Data.Preferred_Name_Data.Name_Detail_Data.First_Name |
| candidate.preferred_last_name_surname.value | Applicant_Data.Personal_Data.Name_Data.Preferred_Name_Data.Name_Detail_Data.Last_Name |
| offer.starts_at | Hire_Employee_Data.Hire_Date |
| offer.starts_at | Hire_Employee_Data.Request_One_Time_Payment_Sub_Process.Effective_Date |
| offer.starts_at | Request_One_Time_Payment_Sub_Process.Request_One_Time_Payment_Data[].Scheduled_Payment_Date |
| offer.bonus_percent.value | Hire_Employee_Data.Propose_Compensation_for_Hire_Sub_Process.Propose_Compensation_for_Hire_Data.Bonus_Plan_Data.Bonus_Plan_Sub_Data[].Individual_Target_Percent |
| offer.bonus_type.type | Hire_Employee_Data.Propose_Compensation_for_Hire_Sub_Process.Propose_Compensation_for_Hire_Data.Bonus_Plan_Data.Bonus_Plan_Sub_Data[].Bonus_Plan_Reference.ID[].$value |
| offer.country.value | Hire_Employee_Data.Propose_Compensation_for_Hire_Sub_Process.Propose_Compensation_for_Hire_Data.Bonus_Plan_Data.Bonus_Plan_Sub_Data[].Bonus_Plan_Reference.ID[].$value |
| offer.country.external_id | Applicant_Data.Personal_Data.Name_Data.Legal_Name_Data.Name_Detail_Data.Country_Reference.ID[].$value |
| offer.country.external_id | Applicant_Data.Personal_Data.Name_Data.Legal_Name_Data.Name_Detail_Data.Country_Reference.ID[].$value |
| offer.currency.value.unit | Hire_Employee_Data.Propose_Compensatoin_for_Hire_Sub_Process.Propose_Compensation_for_Hire_Data.Pay_Plan_Data.Pay_Plan_Sub_Data[].Currency_Reference.ID[].$value |
| offer.currency.value.ammount | Hire_Employee_Data.Propose_Compensation_for_Hire_Sub_Process.Propose_Compensation_for_Hire_Data.Pay_Plan_Data.Pay_Plan_Sub_Data[].Amount |
| offer.cost_center.external_id | Hire_Employee_Data.Edit_Assign_Organization_Sub_Process.Position_Organization_Assignments_Data.Cost_Center_Assignments_Reference[].ID[].$value |
| offer.employment_type.type | Hire_Employee_Data.Edit_Assign_Organization_Sub_Process.Position_Organization_Assignments_Data.Cost_Center_Assignments_Reference[].ID[].$value |
| offer.entity.type | Hire_Employee_Data.Edit_Assign_Organization_Sub_Process.Position_Organization_Assignments_Data.Cost_Center_Assignments_Reference[].ID[].$value |
| offer.entity.external_id | Hire_Employee_Data.Edit_Assign_Organization_Sub_Process.Position_Organization_Assignments_Data.Company_assignments_Reference[].ID[].$value |
| offer.job_specialty.external_id | Hire_Employee_Data.Edit_Assign_Organization_Sub_Process.Position_Organization_Assignments_Data.Custom_Organization_Assignment_Data[].Custom_Organization_Assignment_Reference.ID[].$value |
| offer.locality.external_id | Hire_Employee_Data.Edit_Assign_Organization_Sub_Process.Position_Organization_Assignments_Data.Custom_Organization_Assignment_Data[].Custom_Organization_Assignment_Reference.ID[].$value |
| offer.per_year_or_per_hour.value | Hire_Employee_Data.Hire_Employee_Event_Data.Position_Details.Pay_Rate_Type_Reference |
| offer.per_year_or_per_hour.value | Hire_Employee_Data.Hire_Employee_Event_Data.Position_Details.Pay_Rate_Type_Reference |
| offer.per_year_or_per_hour.value | Hire_Employee_Data.Proposal_Compensation_for_HIre_Sub_Process.Propose_Compensation_for_Hire_Data.Pay_Plan_Data.Pay_Plan_Sub_Data[].Frequency_Reference.ID[].$value |
| offer.rsu__value.value | Hire_Employee_Data.Request_Stock_Grant_Sub_Process.Request_Stock_Grant_Data.Individual_Stock_Grant[].Grant_Amount |
| offer.sales_geo_differential.external_id | Hire_employee_data.Edit_Assign_Organization_Sub_Process.Position_Organization_Assignments_Data.Custom_Organization_Assignment_Data[].Custom_Organization_Assignment_Reference.ID[].$value |
| offer.signing_bonus.value.unit | Hire_Employee_Data.Request_One_Time_Payment_Sub_Process.Request_One_Time_Payment_Data[].Currency_Reference.ID[].$value |
| offer.signing_bonus.value.amount | Hire_Employee_Data.Request_One_Time_Payment_Sub_Process.Request_One_Time_Payment_Data[].Amount |
| offer.state_province.value | Hire_Employee_Data.Hire_Employee_Data.Position_Details.Location_Refernce |
Secutity Components
- Greenhouse use swebhooks with Workato via a secured channel
- Workato calls to Platypus using OAuth credentials saved in Workato
- Platypus calls to Workday via authenticated SOAP operations
- Platypus calls to Slack via API credentials
- Platypus calls to Denomas via Denomas Access Tokens and a Bot Account
- All connections are made via HTTPS
Data Classification
This integration handles and stores Orange Data (Personal data)
Environments
| Environments | Greenhouse | Workato | Platypus | Workday | Slack | Denomas |
|---|---|---|---|---|---|---|
| Production | Production | Production | Production | Production | Production | Production |
| Development | sandbox | Dev | Development | sandbox | dev-sandbox | prod |
176cf9ec)
