Multi-State Withholding
Some employees live in one state and work in another. By default, the state where the employee works taxes their wages. Some neighboring states have a reciprocity agreement that changes this: an employee who lives in one state and works in the other pays state income tax only to the state where they live. Use the Nonresident Certificate parameter on an employee's tax setup to apply that agreement, so that Worklio stops withholding the work state's income tax and withholds the home state's income tax instead.
This page covers the one parameter that controls it and how to set it. For the full employee tax setup, see Employee Tax Setup.
How reciprocity works
A reciprocity agreement is between two states. The employee has to ask for it. They complete the work state's exemption certificate (for example, New Jersey's Form NJ-165 or Pennsylvania's Form REV-419) and give it to you as the employer. Each state publishes its own form, and the form name differs by state.
Reciprocity applies to state income tax only. It doesn't change federal withholding, and an agreement between two states doesn't automatically cover local taxes.
Worklio doesn't publish a list of state pairs here. Check the work state's tax agency to confirm that an agreement exists with the employee's home state, and to find the correct form.
The Nonresident Certificate parameter
Each state entry in the employee's tax setup includes a Nonresident Certificate parameter. It records whether the employee has given you that state's nonresident certificate.
| Property | Value |
|---|---|
name | _HAS_NON_RES_CERT |
caption | Nonresident Certificate |
parameterType | 1 (boolean) |
Valid values | TRUE or FALSE, as listed in the parameter's options |
| Where it lives | In the parameters array of a state entry from Get Employee Tax Setup |
| Default | FALSE. Worklio treats the parameter as FALSE until you set it. |
The response marks the parameter isOptional: false, but a state entry can still return isComplete: true while this parameter has no value. You only need to set it for employees who have a certificate.
The parameter appears in every state entry that supports it. Set it on the entry for the state where the employee works, not on the entry for the state where they live.
The parameter stores only the yes/no flag. It doesn't hold the certificate itself, so keep the employee's completed form on file.
Set the Nonresident Certificate
GET https://api.worklio.com/wep/companies/{CLIENT_ID}/employees/{EMPLOYEE_ID}/taxsetup
POST https://api.worklio.com/wep/companies/{CLIENT_ID}/employees/{EMPLOYEE_ID}/taxsetup
Both calls require a bearer access token (see How to Get API Access). CLIENT_ID is the company identifier returned as id when you create the company. EMPLOYEE_ID is the employee identifier returned as id when you create the employee.
Before you start:
- The employee has a home address in the state where they live.
- The employee is assigned to a company work location in the state where they work. See Create Work Location and Create Employee. This is what adds the work state's entry to their tax setup.
- You have the employee's completed exemption certificate for the work state.
Steps:
- Call Get Employee Tax Setup and find the entry in
statewhosestateis the work state. - In that entry's
parameters, find the parameter withname_HAS_NON_RES_CERTand copy itstaxId. - Call Update Employee Tax Setup with that
taxIdandvalueset toTRUE.
The response doesn't mark which state entry is the home state or the work state. Each entry has only a name and a state. To pick the right entry in step 1, use the state of the work location you assigned to the employee (the workLocationId you sent when you created the employee) and match it against state.
The parameter looks like this in a state entry. This excerpt is from the KY entry of an example response. It has the same shape in every state entry:
{
"taxId": "WuwPB_Tmpa4rXI7tbbsa5pI-0K4oG_e1Ta9l6d6IwxFLR7GaaxUn17Z8@",
"compatibleStates": ["AZ", "FL", "NY", "NJ", "TX", "NC", "GA", "CA", "..."],
"parameterType": 1,
"name": "_HAS_NON_RES_CERT",
"caption": "Nonresident Certificate",
"hint": "Please select if employee has nonresident certificate for this state",
"isOptional": false,
"options": [
{ "caption": "0", "description": "", "value": "FALSE" },
{ "caption": "1", "description": "", "value": "TRUE" }
]
}(compatibleStates is shortened here. The real response lists every compatible state.)
Example request
This example is for an employee who lives in Pennsylvania and works at a company work location in New Jersey. Pennsylvania and New Jersey have a reciprocity agreement, and the employee has given you Form NJ-165. The request sets the Nonresident Certificate on the New Jersey entry.
The code reads the taxId from the GET response instead of hardcoding it, because taxId values are specific to each employee.
require("dotenv").config();
const TOKEN = process.env.API_KEY;
const CLIENT_ID = process.env.CLIENT_ID;
const EMPLOYEE_ID = process.env.EMPLOYEE_ID;
// The state where the employee works. The tax setup response doesn't say which
// entry that is, so use the state of the work location you assigned to the employee.
const WORK_STATE = "NJ";
const url = `https://api.worklio.com/wep/companies/${CLIENT_ID}/employees/${EMPLOYEE_ID}/taxsetup`;
const headers = {
accept: "application/json",
"api-version": "2.0",
authorization: `Bearer ${TOKEN}`,
"content-type": "application/json",
"x-api-version": "2.0"
};
async function setNonresidentCertificate() {
// 1. Read the employee's tax setup
const getResponse = await fetch(url, { method: "GET", headers });
const taxSetup = await getResponse.json();
// 2. Find the Nonresident Certificate parameter in the work state's entry
const workStateEntry = taxSetup.state.find((entry) => entry.state === WORK_STATE);
if (!workStateEntry) throw new Error(`No ${WORK_STATE} entry in this employee's tax setup`);
const parameter = workStateEntry.parameters.find((p) => p.name === "_HAS_NON_RES_CERT");
if (!parameter) throw new Error(`No Nonresident Certificate parameter in the ${WORK_STATE} entry`);
// 3. Set it to TRUE
const response = await fetch(url, {
method: "POST",
headers,
body: JSON.stringify([{ taxId: parameter.taxId, value: "TRUE" }])
});
console.log(response.status); // 204 on success
}
setNonresidentCertificate();TAX_ID=$(curl -s -X GET "https://api.worklio.com/wep/companies/$CLIENT_ID/employees/$EMPLOYEE_ID/taxsetup" \
-H "accept: application/json" \
-H "api-version: 2.0" \
-H "authorization: Bearer $API_KEY" \
-H "x-api-version: 2.0" \
| jq -r '.state[] | select(.state == "NJ") | .parameters[] | select(.name == "_HAS_NON_RES_CERT") | .taxId')
curl -s -X POST "https://api.worklio.com/wep/companies/$CLIENT_ID/employees/$EMPLOYEE_ID/taxsetup" \
-H "accept: application/json" \
-H "api-version: 2.0" \
-H "authorization: Bearer $API_KEY" \
-H "content-type: application/json" \
-H "x-api-version: 2.0" \
-d "[{ \"taxId\": \"$TAX_ID\", \"value\": \"TRUE\" }]"Example response
204 No Content
Success returns an empty body. To confirm the change, call Get Employee Tax Setup and check that the parameter's value is TRUE.
Error response
The update call validates the employee's whole tax setup, not only the parameter you send. If any other required parameter is still unset, the call fails with a 400 and lists the missing parameters by caption. Set the missing parameters in the same request. See Error response on the Employee Tax Setup page for the full response.
If the certificate expires, or the employee stops working in the other state, set the parameter back toFALSE.
If the work state's income tax was withheld before you had the certificate on file, setting the flag doesn't recover it. The employee recovers that tax from the work state by filing a nonresident return.
Related pages
- Employee Tax Setup: read and update all of an employee's federal and state tax parameters.
- Create Work Location: add a company work location in the state where the employee works.
- Create Employee: assign an employee to a work location.
Updated about 1 hour ago
