1 | |
2 | type Paylocity = { |
3 | clientId: string |
4 | clientSecret: string |
5 | } |
6 | |
7 | * Add new employee |
8 | * New Employee API sends new employee data directly to Paylocity Payroll/HR solution. Companies who use the New Hire Template in Paylocity Payroll/HR solution may require additional fields when hiring employees. New Employee API Requests will honor these required fields. |
9 | */ |
10 | export async function main( |
11 | auth: Paylocity, |
12 | companyId: string, |
13 | body: { |
14 | additionalDirectDeposit?: { |
15 | accountNumber?: string |
16 | accountType?: string |
17 | amount?: number |
18 | amountType?: string |
19 | blockSpecial?: false | true |
20 | isSkipPreNote?: false | true |
21 | nameOnAccount?: string |
22 | preNoteDate?: string |
23 | routingNumber?: string |
24 | }[] |
25 | additionalRate?: { |
26 | changeReason?: string |
27 | costCenter1?: string |
28 | costCenter2?: string |
29 | costCenter3?: string |
30 | effectiveDate?: string |
31 | endCheckDate?: string |
32 | job?: string |
33 | rate?: number |
34 | rateCode?: string |
35 | rateNotes?: string |
36 | ratePer?: string |
37 | shift?: string |
38 | }[] |
39 | benefitSetup?: { |
40 | benefitClass?: string |
41 | benefitClassEffectiveDate?: string |
42 | benefitSalary?: number |
43 | benefitSalaryEffectiveDate?: string |
44 | doNotApplyAdministrativePeriod?: false | true |
45 | isMeasureAcaEligibility?: false | true |
46 | } |
47 | birthDate?: string |
48 | companyName?: string |
49 | currency?: string |
50 | customBooleanFields?: { |
51 | category: 'PayrollAndHR' |
52 | label: string |
53 | value: false | true |
54 | }[] |
55 | customDateFields?: { |
56 | category: 'PayrollAndHR' |
57 | label: string |
58 | value: string |
59 | }[] |
60 | customDropDownFields?: { |
61 | category: 'PayrollAndHR' |
62 | label: string |
63 | value: string |
64 | }[] |
65 | customNumberFields?: { |
66 | category: 'PayrollAndHR' |
67 | label: string |
68 | value: number |
69 | }[] |
70 | customTextFields?: { |
71 | category: 'PayrollAndHR' |
72 | label: string |
73 | value: string |
74 | }[] |
75 | departmentPosition?: { |
76 | changeReason?: string |
77 | clockBadgeNumber?: string |
78 | costCenter1?: string |
79 | costCenter2?: string |
80 | costCenter3?: string |
81 | effectiveDate?: string |
82 | employeeType?: string |
83 | equalEmploymentOpportunityClass?: string |
84 | isMinimumWageExempt?: false | true |
85 | isOvertimeExempt?: false | true |
86 | isSupervisorReviewer?: false | true |
87 | isUnionDuesCollected?: false | true |
88 | isUnionInitiationCollected?: false | true |
89 | jobTitle?: string |
90 | payGroup?: string |
91 | positionCode?: string |
92 | reviewerCompanyNumber?: string |
93 | reviewerEmployeeId?: string |
94 | shift?: string |
95 | supervisorCompanyNumber?: string |
96 | supervisorEmployeeId?: string |
97 | tipped?: string |
98 | unionAffiliationDate?: string |
99 | unionCode?: string |
100 | unionPosition?: string |
101 | workersCompensation?: string |
102 | } |
103 | disabilityDescription?: string |
104 | emergencyContacts?: { |
105 | address1?: string |
106 | address2?: string |
107 | city?: string |
108 | country?: string |
109 | county?: string |
110 | email?: string |
111 | firstName: string |
112 | homePhone?: string |
113 | lastName: string |
114 | mobilePhone?: string |
115 | notes?: string |
116 | pager?: string |
117 | primaryPhone?: string |
118 | priority?: string |
119 | relationship?: string |
120 | state?: string |
121 | syncEmployeeInfo?: false | true |
122 | workExtension?: string |
123 | workPhone?: string |
124 | zip?: string |
125 | }[] |
126 | employeeId?: string |
127 | ethnicity?: string |
128 | federalTax?: { |
129 | amount?: number |
130 | exemptions?: number |
131 | filingStatus?: string |
132 | percentage?: number |
133 | taxCalculationCode?: string |
134 | } |
135 | firstName?: string |
136 | gender?: string |
137 | homeAddress?: { |
138 | address1?: string |
139 | address2?: string |
140 | city?: string |
141 | country?: string |
142 | county?: string |
143 | emailAddress?: string |
144 | mobilePhone?: string |
145 | phone?: string |
146 | postalCode?: string |
147 | state?: string |
148 | } |
149 | isHighlyCompensated?: false | true |
150 | isSmoker?: false | true |
151 | lastName?: string |
152 | localTax?: { |
153 | exemptions?: number |
154 | exemptions2?: number |
155 | filingStatus?: string |
156 | residentPSD?: string |
157 | taxCode?: string |
158 | workPSD?: string |
159 | }[] |
160 | mainDirectDeposit?: { |
161 | accountNumber?: string |
162 | accountType?: string |
163 | blockSpecial?: false | true |
164 | isSkipPreNote?: false | true |
165 | nameOnAccount?: string |
166 | preNoteDate?: string |
167 | routingNumber?: string |
168 | } |
169 | maritalStatus?: string |
170 | middleName?: string |
171 | nonPrimaryStateTax?: { |
172 | amount?: number |
173 | exemptions?: number |
174 | exemptions2?: number |
175 | filingStatus?: string |
176 | percentage?: number |
177 | reciprocityCode?: string |
178 | specialCheckCalc?: string |
179 | taxCalculationCode?: string |
180 | taxCode?: string |
181 | } |
182 | ownerPercent?: number |
183 | preferredName?: string |
184 | primaryPayRate?: { |
185 | annualSalary?: number |
186 | baseRate?: number |
187 | beginCheckDate?: string |
188 | changeReason?: string |
189 | defaultHours?: number |
190 | effectiveDate?: string |
191 | isAutoPay?: false | true |
192 | payFrequency?: string |
193 | payGrade?: string |
194 | payRateNote?: string |
195 | payType?: string |
196 | ratePer?: string |
197 | salary?: number |
198 | } |
199 | primaryStateTax?: { |
200 | amount?: number |
201 | exemptions?: number |
202 | exemptions2?: number |
203 | filingStatus?: string |
204 | percentage?: number |
205 | specialCheckCalc?: string |
206 | taxCalculationCode?: string |
207 | taxCode?: string |
208 | } |
209 | priorLastName?: string |
210 | salutation?: string |
211 | ssn?: string |
212 | status?: { |
213 | adjustedSeniorityDate?: string |
214 | changeReason?: string |
215 | effectiveDate?: string |
216 | employeeStatus?: string |
217 | hireDate?: string |
218 | isEligibleForRehire?: false | true |
219 | reHireDate?: string |
220 | } |
221 | suffix?: string |
222 | taxSetup?: { |
223 | fitwExemptNotes?: string |
224 | fitwExemptReason?: string |
225 | futaExemptNotes?: string |
226 | futaExemptReason?: string |
227 | isEmployee943?: false | true |
228 | isPension?: false | true |
229 | isStatutory?: false | true |
230 | medExemptNotes?: string |
231 | medExemptReason?: string |
232 | sitwExemptNotes?: string |
233 | sitwExemptReason?: string |
234 | ssExemptNotes?: string |
235 | ssExemptReason?: string |
236 | suiExemptNotes?: string |
237 | suiExemptReason?: string |
238 | suiState?: string |
239 | taxDistributionCode1099R?: string |
240 | taxForm?: string |
241 | } |
242 | veteranDescription?: string |
243 | webTime?: { |
244 | badgeNumber?: string |
245 | chargeRate?: number |
246 | isTimeLaborEnabled?: false | true |
247 | } |
248 | workAddress?: { |
249 | address1?: string |
250 | address2?: string |
251 | city?: string |
252 | country?: string |
253 | county?: string |
254 | emailAddress?: string |
255 | location?: string |
256 | mailStop?: string |
257 | mobilePhone?: string |
258 | pager?: string |
259 | phone?: string |
260 | phoneExtension?: string |
261 | postalCode?: string |
262 | state?: string |
263 | } |
264 | workEligibility?: { |
265 | alienOrAdmissionDocumentNumber?: string |
266 | attestedDate?: string |
267 | countryOfIssuance?: string |
268 | foreignPassportNumber?: string |
269 | i94AdmissionNumber?: string |
270 | i9DateVerified?: string |
271 | i9Notes?: string |
272 | isI9Verified?: false | true |
273 | isSsnVerified?: false | true |
274 | ssnDateVerified?: string |
275 | ssnNotes?: string |
276 | visaType?: string |
277 | workAuthorization?: string |
278 | workUntil?: string |
279 | } |
280 | } |
281 | ) { |
282 | const url = new URL(`https://dc1prodgwext.paylocity.com/api/v2/companies/${companyId}/employees`) |
283 |
|
284 | const response = await fetch(url, { |
285 | method: 'POST', |
286 | headers: { |
287 | 'Content-Type': 'application/json', |
288 | Authorization: |
289 | 'Bearer ' + |
290 | (await getOAuthToken(auth, 'https://dc1prodgwext.paylocity.com/public/security/v1/token')) |
291 | }, |
292 | body: JSON.stringify(body) |
293 | }) |
294 | if (!response.ok) { |
295 | const text = await response.text() |
296 | throw new Error(`${response.status} ${text}`) |
297 | } |
298 | return await response.json() |
299 | } |
300 |
|
301 | async function getOAuthToken(auth: Paylocity, tokenUrl: string): Promise<string> { |
302 | const params = new URLSearchParams({ |
303 | grant_type: 'client_credentials', |
304 | client_id: auth.clientId, |
305 | client_secret: auth.clientSecret |
306 | }) |
307 |
|
308 | const response = await fetch(tokenUrl, { |
309 | method: 'POST', |
310 | headers: { |
311 | Authorization: 'Basic ' + btoa(`${auth.clientId}:${auth.clientSecret}`), |
312 | 'Content-Type': 'application/x-www-form-urlencoded' |
313 | }, |
314 | body: params.toString() |
315 | }) |
316 |
|
317 | if (!response.ok) { |
318 | const text = await response.text() |
319 | throw new Error(`OAuth token request failed: ${response.status} ${text}`) |
320 | } |
321 |
|
322 | const data = await response.json() |
323 | return data.access_token |
324 | } |
325 |
|