Getting Started

How to Register

To utilize Paycor’s collection of RESTful APIs through the Developer Portal you must be a registered and approved Paycor partner to be provided access.

The Developer Portal is not available to clients at this time, but we invite you to fill out our Client API Interest Form so we can learn more about your data needs and notify you once it is available.

Authorization

Requirements

Paycor uses a flavor of OAuth that requires two items to retrieve an authorization token:

  1. APIm Subscription Key

  2. OAuth App ID and Secret

These items will be provided to you after your registration has been processed.

Getting an Access Token

To retrieve an authorization token, follow the OAuth steps below. Note: Paycor will support the use of API public and private keys until 2020 for our current users of the Developer Portal. See addendum for details for API key support.

  1. After you have signed into the Developer Portal, go to ‘My Account’ and create sandbox OAuth app ID and secret.

  2. Make a POST request must be made to this endpoint and supply the correct scoping value.

    https://api.paycor.com/Accounts/v2/Common/Token
  3. Add the APIm Subscription Key to the header of the request using the “Ocp-Apim-Subscription-Key” header.

  4. Include 'x-www-form-urlencoded' in the body of the request and populate it with the following key value pairs:
    Use cc{CompanyId} or c{ClientId} based of scoping level. cc = Company level scoping vs c = Client level scoping. [Scope Name] is a value given to partner by Paycor team. Values contained in Quotes above are actual values. Values Contained in Square Brackets are Representative values

    Key: grant_type
    Value: “client_credentials”

    Key: client_id
    Value: [OAuth AppId]

    Key: client_secret
    Value: [OAuth Secret]

    Key: scope
    Value: cc[CompanyID] [Scope Name]

    Access Token

    The response will return an access token in JSON. This token has the following properties:

    Property: access_token
    Description: The actual access token to be passed in the “Authorization” header for future requests.

    Property: expires_in
    Description: Amount of time in seconds that the access_token is valid.

    Property: token_type
    Description: Describes the type of the token being returned.

Making an API Request

Both the APIm Subscription Key and the access token obtained from the above are required to be passed in any request to Paycor APIs. The access token will populate as follows:  

“Authorization”: “bearer [access_token]”

Sandbox Access

Use the Sandbox to access the API resources without making changes to production data. When accessing Paycor’s Sandbox, the endpoints will be same as the production URL’s, as the traffic is routed to the Sandbox system based on the Subscription Key used.

A separate Subscription Key will be available along with API Key that will be used in production. In the Sandbox, insert, delete, and resource updates will work just like in production. Normal API responses will be provided (Example: status, error, objects, etc.).

API Standards

Paycor supports a set of RESTful APIs. In general, the resources support operations using the five main HTTP verbs:

Verbs: GET
Definition: retrieves a single resource or a trust of resources

Verbs: POST
Definition: creates a new resource

Verbs: PUT
Definition: updates an existing resource

Verbs: DELETE
Definition: removal of an existing resource

Verbs: PATCH
Definition: partial update of an existing resource

Meaningful Descriptions

Paycor uses meaningful descriptions instead of codes and types for payload fields. For example, the gender field should have the values “Male” and “Female” instead of M or F. This makes our resources easier to read and consume for users.

Routes and Resources

Each resource (Example: Employee, Client, Rate, Earning, etc.) will have a single identifier when it comes to creating the route for the resource. For example: GET v1/employees/:id

The route is critical to utilizing an operation. Each route follows this pattern:

{version}/{resource}/{uniqueIdentifier}/{sub resource | optional}

Here are examples of Paycor operations that may be used:

Resource: Employees
Domain: Employee
Route: v1/employees/:id

Resource: Earnings
Domain: Payroll
Route: v1/employees/:id/earnings/:id

Resource: Taxes
Domain: Payroll
Route: v1/employees/:id/taxes/:id

Resource: Deductions
Domain: Payroll
Route: v1/employees/:id/deductions/:id

Resource: PayRates
Domain: Payroll
Route: v1/employees/:id/rates/:id

Resource: DirectDeposit
Domain: Payroll
Route: v1/employees/:id/directDeposits/:id

Components of a Request

Header

The Accept header allows the consumer of an API to choose what format the data should be returned. Our APIs support both application/JSON and application/XML. By default, JSON is returned. The standard way that JSON is formatted is camel cased (example “firstName”). 

URLs

The URL is the location of major version changes for resources and operations as shown below:

(v1/employees/:id )

Based on the operation, the request may contain mandatory and optional parameters. Meta-data information is provided for each parameter to tell its datatype, whether it’s mandatory or optional, and a description to enable users in understanding the use of the parameter.

Body

Resource creation (CREATE) and update (PUT) require the information to be passed by the request Body.

Review the example below:

[{
'id': 0,
'clientId': 0,
'code': 0.0,
'description': 'string',
'parentCode': 0.0
}]

Paging

Many of the list operations support paging functionality using the “page” and “pageSize” query string parameters. 

In the following example, the second page is requested and the page size is set to 10. This means that 10 items will be returned at a time based on this request.

 https://api.paycor.com/employee/v2/employees?page=2&pageSize=10

Additional paging support is supplied via the Link Header. Using the Link Header will exclude this meta-data from the response itself. The Link Header provides quick links to the next set of data as well as the previous set of data. The HTTP Link Header specification has two parts: the first is the link itself, and the second is a “relationship name” indicated by the “rel” attribute. Paycor uses the standard relationship name of “next” and “prev” to indicate the next and previous set of data.

Review the example below:

Link: < https://api.paycor.com/employee/v2/employees?page=3&pageSize=10>, rel='next',
< https://api.paycor.com/employee/v2/employees?page=1&pageSize=10>, rel='prev'

In addition to the Link Header, Paycor uses a custom header for total rows. This allows consuming applications to get the total count of rows in order to determine how to paginate.  The response header name is: “X-TotalCount”.

Sorting

Paycor uses a single query string parameter named “Sort”. The value is a comma separated list of fields that are sorted. Use the “-“ character before the sort field to denote descending sort.

In the following example, the results will be sorted by lastName ascending and birthDate descending:

https://api.paycor.com/employee/v2/employees?sort='lastName,-birthDate'

Filtering

To filter data, use the query string as opposed to route data. At a minimum, each resource list query should allow for filtering on key properties of the resource.

Review the example below:

https://api.paycor.com/employee/v2/employees?clientId=102&employeeNumber=1

Response Codes

The following response codes are supported via Paycor APIs:

Status: 200
Name: OK
Description: Response to a successful GET, PUT, PATCH or DELETE. Can also be used

Status: 201
Name: Created
Description: Response to a POST that results in a creation. Should be combined with a Location header pointing to the location of the new resource (location header is optional at this point

Status: 204
Name: No Content
Description: Response to a successful request that won't be returning a body (like a DELETE request)

Status: 304
Name: Not Modified
Description: Used when HTTP caching headers are in play

Status: 400
Name: Bad Request
Description: The request is malformed, such as if the body does not parse

Status: 401
Name: Unauthorized
Description: When no or invalid authentication details are provided. Also useful to trigger an auth popup if the API is used from a browser

Status: 403
Name: Forbidden
Description: When authentication succeeded but authenticated user doesn't have access to the resource

Status: 404
Name: Not Found
Description: When a non-existent resource is requested

Status: 405
Name: Method Not Allowed
Description: When an HTTP method is being requested that isn't allowed for the authenticated user

Status: 410
Name: Gone
Description: Indicates that the resource at this end point is no longer available. Useful as a blanket response for old API versions

Status: 415
Name: Unsupported Media Type
Description: If incorrect content type was provided as part of the request

Status: 422
Name: Unprocessable
Description: EntityUsed for validation errors

Status: 429
Name: Too Many Requests
Description: When a request is rejected due to rate limiting

Status: 500
Name: Internal Server Error
Description: Used for any unhandled exception.

Addendum

API Public and Private Keys:

  1. If your access has been provisioned using public/private keys, a GET request must be made using the HMAC signature on a private key:

    https://api.paycor.com/Accounts/v2/GetAccessToken
  2. Add the APIm Subscription Key to the header of the request using the “Ocp-Apim-Subscription-Key” header.

  3. Compute the HMAC signature using the API private key as shown in the C# snippet below:

    public override async Task<IRestResponse> ExecuteTaskAsync(IRestRequest request)
    {
    var date = DateTime.UtcNow;
    request.AddHeader("Date", date.ToString("R"));
    var stringToSign = CreateStringToSign(request, date);
    var signature = GetHMACSignature(_apiSecretKey, stringToSign);
    var authstring = string.Format("paycorapi {0}:{1}", _apiKey, signature);
    request.AddHeader("Authorization", authstring);
    IRestResponse response = null;

    for (var retry = 0; retry < _maxRetries; retry++)
    {
    response = await base.ExecuteTaskAsync(request);

    if (response.StatusCode == HttpStatusCode.OK
    || response.StatusCode == HttpStatusCode.Unauthorized
    || response.StatusCode == HttpStatusCode.Forbidden)
    {
    return response;
    }

    Thread.Sleep(_retryDelaySeconds * 1000);
    }
    return response;
    }

    private string CreateStringToSign(IRestRequest request, DateTime date)
    {
    var sb = new StringBuilder();
    sb.AppendLine(request.Method.ToString());
    var body = request.Parameters.FirstOrDefault(p => p.Type == ParameterType.RequestBody);

    if (body != null)
    {
    sb.AppendLine(string.Empty);

    if (request.RequestFormat == DataFormat.Xml)
    {
    sb.AppendLine(request.XmlSerializer.ContentType);
    }
    else if (request.RequestFormat == DataFormat.Json)
    {
    sb.AppendLine(request.JsonSerializer.ContentType);
    }
    else
    {
    sb.AppendLine(string.Empty);
    }
    }
    else
    {
    sb.AppendLine(string.Empty);
    sb.AppendLine(string.Empty);
    }

    sb.AppendLine(date.ToString("R"));
    var pathAndQuery = BuildUri(request).PathAndQuery;

    if (!string.IsNullOrEmpty(_removeFromEncryptionUrl))
    {
    pathAndQuery = pathAndQuery.Replace(_removeFromEncryptionUrl, "");
    }

    sb.AppendLine(pathAndQuery);
    var stringToSign = sb.ToString();

    return stringToSign;
    }

    private static string GetHMACSignature(string privatekey, string message)
    {
    var encoding = new ASCIIEncoding();
    var keyByte = encoding.GetBytes(privatekey);
    var hmacsha1 = new System.Security.Cryptography.HMACSHA1(keyByte);
    var messageBytes = encoding.GetBytes(message);
    var hashmessage = hmacsha1.ComputeHash(messageBytes);

    return Convert.ToBase64String(hashmessage);
    }

    Note: A working code snippet for HMAC Computation can be provided if needed in C#