Partner API is a public-facing API provided by Justworks to partners and customers who want to integrate with Justworks.
Our V1 API uses OAuth2 authentication.
The base URL for the V1 API is https://public-api.justworks.com.
To obtain access to the API, please contact partner-api@justworks.com.
Our API uses scoped tokens. When a token is assigned a scope, it will inherit all the access permissions of its "child scope(s)".
Scope Name | Child Scope(s) |
---|---|
company.detail:read | company.basic:read |
company.basic:read | |
company.bank_account:read | |
member.detail:read | member.basic:read |
member.basic:read | |
member.dob:read | |
member.sex:read | |
member.employment:read | |
member.pay:read | |
member.tax_id:read | |
payroll:read | |
paystub:read | |
time_off:read | |
deductions:write | deductions:read |
deductions:read |
Currency values are represented as zero-decimal.
Fixed amounts use 2 significant digits. For example, $45.00
would be represented in the API as 4500
.
Percentage amounts use 4 significant digits. For example, 3.7%
would be represented as 37000
.
Our API design follows the guidelines below regarding empty values:
required
in our API docs if we always expect it to have a value. These fields will never be omitted from responses.The V1 API is built with international data in mind. This includes addresses, phone numbers and money fields.
There are also more complex structures that require specific shapes of data and fieldsets that vary from country to country. Such structures include a company's legal corporate structure, tax identifiers and member employment information.
On these types you'll find a hashmap keyed by country code. This map contains typed structures that match the way specific countries track things like corporate legal structure, tax IDs and employment status.
We represent country codes using the ISO 3166-1 alpha-2 standard.
International members (members with a type
of international_employee
or employee_eor
) are now included in List Members and Get Member datasets.
These members contain a limited subset of datapoints, including the following:
Justworks's Public API is built around an Authorization Code Flow; customers must explicitly authorize integrations to access their data.
Creation of OAuth Applications for our API is not self-service, but the Justworks team is happy to help set one up for you.
We will need the following information:
We also support the following optional info, shown to customers when they authorize your application:
Once created, you will be provided with a client_id
and client_secret
for your application, along with the Justworks auth url customers should be directed to.
You will need to construct a URL and direct customers to it, so that they can authorize you to access their information. A common way to do this is to have a button in your UI saying something like "Connect to Justworks".
It is recommended to use an OAuth library to construct the URL, but it will look something like https://payroll.justworks.com/oauth/authorize?response_type=code&client_id=CLIENT_ID&state=STATE_PARAM&scope=SPACE_DELIMITED_SCOPES&redirect_uri=REDIRECT_URI
, where:
code
The customer will access this link in their browser, log into their Justworks account, and authorize your application to access their information.
Once this is successful, they will be redirected to the specified redirect_uri
, with the generated authorization code in the code
query string parameter and the state
parameter returned for verification.
The redirect will call your callback URL with an authorization code; your system can now call the API directly, and exchange this for an OAuth Token.
curl --location 'https://public-api.justworks.com/oauth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'client_id=CLIENT_ID' \
--data-urlencode 'client_secret=CLIENT_SECRET' \
--data-urlencode 'redirect_uri=REDIRECT_URI' \
--data-urlencode 'code=AUTHORIZATION_CODE'
The parameters for this request:
grant_type
should always be authorization_code
when obtaining a token for the first time (i.e., not refreshing a token)client_id
is the client ID of the OAuth application obtained in Step 1client_secret
the client secret of the OAuth application obtained in Step 1redirect_uri
should match the Redirect URI used in the previous stepcode
is the authorization code obtained in the previous stepSample Response: { "access_token": "ACCESS_TOKEN", "refresh_token": "REFRESH_TOKEN", "scope": "SPACE DELIMITED SCOPES", "expires_at": "2024-12-11T13:09:16.688468-06:00" }
Access tokens are valid for 24 hours. Refresh tokens are valid for 30 days.
You can now access the API to retrieve customer data. Simply add the access token to the header with the Bearer prefix:
curl --location 'https://public-api.justworks.com/v1/company' --header 'Authorization: Bearer ACCESS_TOKEN'
Because the access token is short-lived, the refresh token will need to be regularly used to obtain a new access token.
Example refresh request:
curl --location 'https://public-api.justworks.com/oauth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=CLIENT_ID' \
--data-urlencode 'client_secret=CLIENT_SECRET' \
--data-urlencode 'grant_type=refresh_token' \
--data-urlencode 'refresh_token=REFRESH_TOKEN'
Parameters:
grant_type
should always be refresh_token
when refreshing a tokenclient_id
is the client ID of the OAuth application obtained in Step 1client_secret
is the client secret of the OAuth application obtained in Step 1refresh_token
the refresh_token value from the original token response in Step 3Response:
{
"access_token": "ACCESS_TOKEN",
"refresh_token": "REFRESH_TOKEN",
"scope": "SPACE DELIMITED SCOPES",
"expires_at": "2024-12-11T13:09:16.688468-06:00"
}
When a customer disconnects your application, you should revoke the access token to prevent further access to their data.
curl --location 'https://public-api.justworks.com/v1/oauth/revoke' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=CLIENT_ID' \
--data-urlencode 'client_secret=CLIENT_SECRET' \
--data-urlencode 'token={ACCESS_TOKEN|REFRESH_TOKEN}' \
--data-urlencode 'token_type_hint={access_token|refresh_token}'
The parameters for this request:
client_id
is the client ID of the OAuth application obtained in Step 1client_secret
is the client secret of the OAuth application obtained in Step 1token
is the access token or refresh token you want to revoketoken_type_hint
is optional, but recommended. Both access_token
and refresh_token
will be looked up at the best effort, but specifying the type will allow us to optimize the lookup. access_token
refresh_token
Note: The older /oauth/revoke endpoint is deprecated and will be removed in the future. Please always use /v1/oauth/revoke
client_id required | string Client ID |
client_secret required | string Client secret |
grant_type required | string Enum: "authorization_code" "refresh_token" Grant type |
scope required | string Requested scopes (space-delimited list) |
redirect_uri required | string Redirect URI |
code | string Code (include if |
refresh_token | string Refresh token (include if |
{- "token_type": "string",
- "access_token": "string",
- "expires_at": "2019-08-24T14:15:22Z",
- "refresh_token": "string",
- "scope": "string"
}
client_id required | string Client ID |
client_secret required | string Client secret |
token required | string Token |
token_type_hint required | string Enum: "access_token" "refresh_token" Token type hint |
Returns data about the current company.
Scope | Enforcement | Level |
---|---|---|
company.basic:read | REQUIRED |
Endpoint |
company.detail:read | OPTIONAL | Field |
See response field descriptions for details about field level requirements.
{- "id": "string",
- "tax_id": "string",
- "display_name": "string",
- "legal_name": "string",
- "addresses": [
- {
- "type": "home",
- "city": "string",
- "country_code": "string",
- "line1": "string",
- "line2": "string",
- "postal_code": "string",
- "zone_code": "string"
}
], - "departments": [
- {
- "id": "string",
- "name": "string"
}
], - "offices": [
- {
- "id": "string",
- "name": "string",
- "address": {
- "type": "home",
- "city": "string",
- "country_code": "string",
- "line1": "string",
- "line2": "string",
- "postal_code": "string",
- "zone_code": "string"
}
}
], - "phones": [
- {
- "type": "home",
- "phone": "string"
}
], - "country_data": {
- "US": {
- "structure": "corporation"
}
}, - "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}
Returns the current company's bank account information.
Scope | Status | Info |
---|---|---|
company.bank_account:read | REQUIRED |
Endpoint level requirement. |
{- "bank_name": "string",
- "account_type": "string",
- "account_number_tail": "string",
- "routing_number": "string"
}
List custom fields for the current company.
Scope | Status | Info |
---|---|---|
company.detail:read | REQUIRED |
Endpoint level requirement. |
{- "items": [
- {
- "id": "string",
- "label": "string",
- "type": "text",
- "options": [
- "string"
], - "cardinality": "single",
- "category": "personal"
}
]
}
Returns a list of the current company's members.
Scope | Enforcement | Level |
---|---|---|
member.basic:read | REQUIRED |
Endpoint |
member.detail:read | OPTIONAL | Field |
member.employment:read | OPTIONAL | Field |
member.pay:read | OPTIONAL | Field |
See response field descriptions for details about field level requirements.
cursor | string Example: cursor=cursor Cursor |
limit | integer <int32> [ 1 .. 100 ] Example: limit=10 Limit |
status | string Example: status=active Status |
updated_at | string <date> Example: updated_at=2021-01-22 Date format: YYYY-MM-DD |
updated_at_gt | string <date> Example: updated_at_gt=2021-01-22 Date format: YYYY-MM-DD |
updated_at_gte | string <date> Example: updated_at_gte=2021-01-22 Date format: YYYY-MM-DD |
updated_at_lt | string <date> Example: updated_at_lt=2021-01-22 Date format: YYYY-MM-DD |
updated_at_lte | string <date> Example: updated_at_lte=2021-01-22 Date format: YYYY-MM-DD |
updated_at_ne | string <date> Example: updated_at_ne=2021-01-22 Date format: YYYY-MM-DD |
{- "items": [
- {
- "id": "string",
- "company_id": "string",
- "work_id": "string",
- "name": {
- "family_name": "string",
- "full_preferred_name": "string",
- "given_name": "string",
- "preferred_name": "string",
- "additional_names": "string"
}, - "active": true,
- "employment_location_type": "remote",
- "type": "unpaid_owner",
- "addresses": [
- {
- "type": "home",
- "city": "string",
- "country_code": "string",
- "line1": "string",
- "line2": "string",
- "postal_code": "string",
- "zone_code": "string"
}
], - "employment_history": [
- {
- "status": "active",
- "type": "unpaid_owner",
- "effective_date": "2019-08-24",
- "updated_at": "2019-08-24T14:15:22Z"
}
], - "pay_history": [
- {
- "pay_rate": 2147483647,
- "country_data": {
- "US": {
- "flsa_status": "exempt"
}
}, - "effective_date": "2019-08-24",
- "pay_basis": "annual",
- "pay_currency": "string"
}
], - "phones": [
- {
- "type": "home",
- "phone": "string"
}
], - "current_employment": {
- "status": "active",
- "type": "unpaid_owner",
- "effective_date": "2019-08-24",
- "updated_at": "2019-08-24T14:15:22Z"
}, - "current_pay": {
- "pay_rate": 2147483647,
- "country_data": {
- "US": {
- "flsa_status": "exempt"
}
}, - "effective_date": "2019-08-24",
- "pay_basis": "annual",
- "pay_currency": "string"
}, - "current_pay_frequency": "semimonthly",
- "date_added_to_justworks": "2019-08-24",
- "date_of_birth": "2019-08-24",
- "department": {
- "id": "string",
- "name": "string"
}, - "employment_start_date": "2019-08-24",
- "employment_termination_date": "2019-08-24",
- "job_title": "string",
- "manager": {
- "id": "string",
- "full_preferred_name": "string"
}, - "office": {
- "id": "string",
- "name": "string",
- "address": {
- "type": "home",
- "city": "string",
- "country_code": "string",
- "line1": "string",
- "line2": "string",
- "postal_code": "string",
- "zone_code": "string"
}
}, - "sex_assigned_at_birth": "",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}
], - "next_cursor": "string"
}
Get a member by ID within the current company.
Scope | Enforcement | Level |
---|---|---|
member.basic:read | REQUIRED |
Endpoint |
member.detail:read | OPTIONAL | Field |
member.dob:read | OPTIONAL | Field |
member.sex:read | OPTIONAL | Field |
member.employment:read | OPTIONAL | Field |
member.pay:read | OPTIONAL | Field |
See response field descriptions for details about field level requirements.
member_id required | string Example: member_01j6feh5sp5jr16vtnp95kyqby Member ID |
{- "id": "string",
- "company_id": "string",
- "work_id": "string",
- "name": {
- "family_name": "string",
- "full_preferred_name": "string",
- "given_name": "string",
- "preferred_name": "string",
- "additional_names": "string"
}, - "active": true,
- "employment_location_type": "remote",
- "type": "unpaid_owner",
- "addresses": [
- {
- "type": "home",
- "city": "string",
- "country_code": "string",
- "line1": "string",
- "line2": "string",
- "postal_code": "string",
- "zone_code": "string"
}
], - "employment_history": [
- {
- "status": "active",
- "type": "unpaid_owner",
- "effective_date": "2019-08-24",
- "updated_at": "2019-08-24T14:15:22Z"
}
], - "pay_history": [
- {
- "pay_rate": 2147483647,
- "country_data": {
- "US": {
- "flsa_status": "exempt"
}
}, - "effective_date": "2019-08-24",
- "pay_basis": "annual",
- "pay_currency": "string"
}
], - "phones": [
- {
- "type": "home",
- "phone": "string"
}
], - "current_employment": {
- "status": "active",
- "type": "unpaid_owner",
- "effective_date": "2019-08-24",
- "updated_at": "2019-08-24T14:15:22Z"
}, - "current_pay": {
- "pay_rate": 2147483647,
- "country_data": {
- "US": {
- "flsa_status": "exempt"
}
}, - "effective_date": "2019-08-24",
- "pay_basis": "annual",
- "pay_currency": "string"
}, - "current_pay_frequency": "semimonthly",
- "date_added_to_justworks": "2019-08-24",
- "date_of_birth": "2019-08-24",
- "department": {
- "id": "string",
- "name": "string"
}, - "employment_start_date": "2019-08-24",
- "employment_termination_date": "2019-08-24",
- "job_title": "string",
- "manager": {
- "id": "string",
- "full_preferred_name": "string"
}, - "office": {
- "id": "string",
- "name": "string",
- "address": {
- "type": "home",
- "city": "string",
- "country_code": "string",
- "line1": "string",
- "line2": "string",
- "postal_code": "string",
- "zone_code": "string"
}
}, - "sex_assigned_at_birth": "",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}
List custom field values for a given memberId for the current company.
Scope | Status | Info |
---|---|---|
member.detail:read | REQUIRED |
Endpoint level requirement. |
member_id required | string Example: member_01j6feh5sp5jr16vtnp95kyqby Member ID |
{- "items": [
- {
- "id": "string",
- "values": [
- "string"
]
}
]
}
Get the Tax ID of the specified member.
Scope | Status | Info |
---|---|---|
member.tax_id:read | REQUIRED |
Endpoint level requirement. |
member_id required | string Example: member_01j6feh5sp5jr16vtnp95kyqby Member ID |
{- "US": {
- "ssn": "string"
}
}
List payrolls for the current company.
Scope | Status | Info |
---|---|---|
payroll:read | REQUIRED |
Endpoint level requirement. |
start_date required | string Example: start_date=2021-01-01 Start Date |
end_date required | string Example: end_date=2021-01-31 End Date |
limit | integer <int32> [ 1 .. 100 ] Example: limit=10 Limit |
cursor | string Example: cursor=cursor Cursor |
{- "items": [
- {
- "id": "string",
- "company_debit": -2147483648,
- "employee_taxes": 2147483647,
- "employer_taxes": 2147483647,
- "gross_pay": 2147483647,
- "net_pay": 2147483647,
- "currency": "string",
- "debit_date": "string",
- "pay_date": "string",
- "payment_method": "bank_account"
}
], - "next_cursor": "string"
}
List fees for a given payrollId for the current company.
Scope | Status | Info |
---|---|---|
payroll:read | REQUIRED |
Endpoint level requirement. |
payroll_id required | string Example: payroll_01j6feh5sp5jr16vtnp95kyqby Payroll ID |
limit | integer <int32> [ 1 .. 100 ] Example: limit=10 Limit |
cursor | string Example: cursor=cursor Cursor |
{- "items": [
- {
- "member_id": "string",
- "reference_id": "string",
- "name": "string",
- "reference_type": "member",
- "type": "payment_fee_first",
- "amount": 2147483647,
- "currency": "string"
}
], - "next_cursor": "string"
}
List paystubs for a given payrollId for the current company.
Scope | Status | Info |
---|---|---|
paystub:read | REQUIRED |
Endpoint level requirement. |
payroll_id required | string Example: payroll_01j6feh5sp5jr16vtnp95kyqby Payroll ID |
limit | integer <int32> [ 1 .. 100 ] Example: limit=10 Limit |
cursor | string Example: cursor=cursor Cursor |
{- "items": [
- {
- "member_id": "string",
- "paystub_id": "string",
- "gross_pay": 2147483647,
- "net_pay": 2147483647,
- "currency": "string",
- "pay_date": "string",
- "pay_group": "string",
- "pay_period_end": "string",
- "pay_period_start": "string"
}
], - "next_cursor": "string"
}
Get a paystub within the current company by ID.
Scope | Status | Info |
---|---|---|
paystub:read | REQUIRED |
Endpoint level requirement. |
paystub_id required | string Example: c1f7d719-c2c0-4b55-9cf7-85de2f4c6498 Paystub ID |
{- "member_id": "string",
- "paystub_id": "string",
- "gross_pay": 2147483647,
- "net_pay": 2147483647,
- "earnings": [
- {
- "name": "string",
- "type": "string",
- "amount": 2147483647,
- "currency": "string"
}
], - "employee_deductions": [
- {
- "name": "string",
- "type": "string",
- "amount": 2147483647,
- "category": "pretax",
- "currency": "string"
}
], - "employer_contributions": [
- {
- "name": "string",
- "type": "string",
- "amount": 2147483647,
- "category": "tax",
- "currency": "string"
}
], - "currency": "string",
- "pay_date": "string",
- "pay_group": "string",
- "pay_period_end": "string",
- "pay_period_start": "string"
}
List deductions for the current company.
Scope | Status | Info |
---|---|---|
deductions:read | REQUIRED |
Endpoint level requirement. |
deduction_type | string Example: deduction_type=health_insurance Deduction Type |
frequency | string Example: frequency=one_time Frequency |
description | string Example: description=description Description |
member_id | string Example: member_id=member_01j6feh5sp5jr16vtnp95kyqby Member ID |
cursor | string Example: cursor=cursor Cursor |
limit | integer <int32> [ 1 .. 100 ] Example: limit=10 Limit |
{- "items": [
- {
- "id": "string",
- "member_id": "string",
- "amount_type": "fixed",
- "amount": 2147483647,
- "currency": "string",
- "deduction_type_code": "string",
- "description": "string",
- "end_date": "2019-08-24",
- "frequency": "one_time",
- "start_date": "2019-08-24"
}
], - "next_cursor": "string"
}
Create deduction for a given company member.
Scope | Status | Info |
---|---|---|
deductions:write | REQUIRED |
Endpoint level requirement. |
Request body
Array of objects (CreateDeductionsRequestItem) |
{- "items": [
- {
- "member_id": "string",
- "operation_id": "string",
- "amount_type": "fixed",
- "amount": 2147483647,
- "currency": "string",
- "deduction_type_code": "string",
- "description": "string",
- "end_date": "2019-08-24",
- "frequency": "one_time",
- "start_date": "2019-08-24"
}
]
}
{- "items": [
- {
- "deduction_id": "string",
- "member_id": "string",
- "operation_id": "string",
- "error_message": "string",
- "success": true
}
]
}
Update deductions by ID.
Scope | Status | Info |
---|---|---|
deductions:write | REQUIRED |
Endpoint level requirement. |
Request body
Array of objects (UpdateDeductionsRequestItem) |
{- "items": [
- {
- "deduction_id": "string",
- "amount_type": "fixed",
- "amount": 2147483647,
- "currency": "string",
- "deduction_type_code": "string",
- "description": "string",
- "end_date": "2019-08-24",
- "frequency": "one_time",
- "start_date": "2019-08-24"
}
]
}
{- "items": [
- {
- "deduction_id": "string",
- "member_id": "string",
- "operation_id": "string",
- "error_message": "string",
- "success": true
}
]
}
Cancel deductions by ID.
Scope | Status | Info |
---|---|---|
deductions:write | REQUIRED |
Endpoint level requirement. |
Request body
deduction_ids required | Array of strings |
{- "deduction_ids": [
- "string"
]
}
{- "items": [
- {
- "deduction_id": "string",
- "member_id": "string",
- "operation_id": "string",
- "error_message": "string",
- "success": true
}
]
}
Get the canonical ID for a company given a legacy company ID.
NOTE: This endpoint is only available temporarily, and will be removed in the future. It is intended to help partners transition to the new ID format, but not to be integrated into ongoing API usage.
legacy_company_id required | string Example: 2fcbdb33-35ce-4c51-af3d-67cee0bf7a42 Legacy Company ID |
{- "id": "string"
}
Get the canonical ID for a member given a legacy member ID.
NOTE: This endpoint is only available temporarily, and will be removed in the future. It is intended to help partners transition to the new ID format, but not to be integrated into ongoing API usage.
legacy_member_id required | string Example: 2fcbdb33-35ce-4c51-af3d-67cee0bf7a42 Legacy Member ID |
{- "id": "string"
}
Generate a time off balances report for the current company.
Scope | Status | Info |
---|---|---|
time_off:read | REQUIRED |
Endpoint level requirement. |
Request body
member_id | string |
policy_id | string |
object (date.Date) |
{- "member_id": "string",
- "policy_id": "string",
- "as_of_date": {
- "time.Time": "string"
}
}
{- "report_id": "string"
}
Get a time off balances report for the current company.
Scope | Status | Info |
---|---|---|
time_off:read | REQUIRED |
Endpoint level requirement. |
report_id required | string Example: 01ARZ3NDEKTSV4RRFFQ69G5FAV Report ID |
{- "status": "pending",
- "items": [
- {
- "member_id": "string",
- "policy_id": "string",
- "unit_type": "minutes",
- "available": 0.1,
- "used": 0.1
}
], - "message": "string"
}
List time off policies for the current company.
Scope | Status | Info |
---|---|---|
time_off:read | REQUIRED |
Endpoint level requirement. |
status | string Example: status=active Status |
limit | integer <int32> [ 1 .. 100 ] Example: limit=10 Limit |
cursor | string Example: cursor=cursor Cursor |
{- "items": [
- {
- "id": "string",
- "name": "string",
- "status": "active",
- "type": "vacation",
- "deactivation_date": "2019-08-24",
- "effective_date": "2019-08-24"
}
], - "next_cursor": "string"
}
List time off requests for the current company.
Scope | Status | Info |
---|---|---|
time_off:read | REQUIRED |
Endpoint level requirement. |
start_date required | string Example: start_date=2021-01-01 Start Date |
end_date required | string Example: end_date=2021-01-31 End Date |
member_id | string Example: member_id=member_01j6feh5sp5jr16vtnp95kyqby Member ID |
policy_id | string Example: policy_id=policy_01j6feh5sp5jr16vtnp95kyqby Policy ID |
status | string Example: status=requested Status |
limit | integer <int32> [ 1 .. 100 ] Example: limit=10 Limit |
cursor | string Example: cursor=cursor Cursor |
{- "items": [
- {
- "id": "string",
- "member_id": "string",
- "policy_id": "string",
- "status": "requested",
- "unit_type": "minutes",
- "amount": 0.1,
- "end_date": "2019-08-24",
- "notes": "string",
- "start_date": "2019-08-24",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}
], - "next_cursor": "string"
}