Payroll Changes API Guide

What this API does

The Payroll Changes API lets you manage one-time payroll adjustments for team members. Use it for additions to regular payroll, such as bonuses, commissions, allowances, and insurance contributions.

Expense reimbursements are not payroll changes. Use the Expenses API for those.

Payroll changes are tied to an engagement, scheduled for a specific month and year, and move through states until they are applied to payroll or canceled. For more information about how Payroll Changes work in general at Oyster, see Payroll Changes at Oyster.


Key concepts

ConceptMeaning
EngagementThe employment relationship associated with the payroll change
TypeThe category of adjustment, such as BONUS or COMMISSION
AmountThe monetary value, using the engagement’s salary currency
Month & YearThe payroll month when the change should be applied
StateCurrent status: PLANNED, APPLIED, or CANCELED

The engagement.type field in responses is either EMPLOYMENT or CONTRACT.


Endpoints at a glance

ActionMethodEndpointNotes
List payroll changesGET/v1/payroll_changesSupports filtering by engagement and payroll period
Retrieve payroll changeGET/v1/payroll_changes/:idReturns one payroll change
Create payroll changePOST/v1/payroll_changesCreates a new PLANNED payroll change
Update payroll changePATCH/v1/payroll_changes/:idOnly updates the amount
Cancel payroll change with reasonPOST/v0.1/payroll_changes/:id/cancelRequires a cancellation reason
Delete payroll changeDELETE/v1/payroll_changes/:idCancels with default reason: Cancelled via API

Payroll change lifecycle

Payroll changes typically move through this flow:

  1. PLANNED
    • Initial state after creation.
    • The change is scheduled for a future or current open payroll period.
  2. APPLIED
    • The change has been applied to payroll processing.
  3. CANCELED
    • The change has been canceled and will not be processed.
    • Cancellation is permanent.

Deprecated states may appear in legacy data: PAID, PENDING, and COMMITTED.


Supported payroll change types

API typeDescription
BONUSOne-time bonus payment
COMMISSIONSales commission or performance payment
EMPLOYER_PENSION_RETIREMENT_CONTRIBUTIONEmployer pension or retirement contribution
HEALTH_INSURANCEHealth insurance supplement
RISK_INSURANCERisk or life insurance supplement
TRAVEL_ALLOWANCETravel allowance
WORK_FROM_HOME_ALLOWANCEWork from home allowance
WELLBEINGHealth and wellbeing allowance
MEALSMeals or sustenance allowance

The list of supported types can also be retrieved at runtime from GET /v1/payroll_changes/sources/types.

Invalid types return a 422 Unprocessable Entity error with a message like:

{
  "errors": [
    {
      "message": "Type must have a valid type."
    }
  ]
}

Listing payroll changes

Use this endpoint to retrieve payroll changes for your company.

GET /v1/payroll_changes

Query parameters

ParameterTypeRequiredDescription
engagementIdstringNoFilter by engagement ID
fromstringNoStart payroll period in YYYY-MM format, inclusive
tostringNoEnd payroll period in YYYY-MM format, inclusive
perPageintegerNoResults per page. Default and maximum are both 100
pageintegerNoPage number, starting at 1

Example request

GET /v1/payroll_changes?engagementId=eng_abc123&from=2024-01&to=2024-12

Example response

{
  "data": [
    {
      "id": "psc_xyz789",
      "type": "BONUS",
      "engagement": {
        "id": "eng_abc123",
        "name": "John Smith",
        "type": "EMPLOYMENT"
      },
      "amount": {
        "decimal": "500.00",
        "currencyCode": "USD"
      },
      "month": "3",
      "year": "2024",
      "notes": "Q1 performance bonus",
      "state": "PLANNED"
    }
  ],
  "meta": {
    "items": 100,
    "count": 1,
    "page": 1,
    "pages": 1
  }
}

Filtering guidance

  • Use from and to to filter by scheduled payroll period.
  • Both dates must use YYYY-MM.
  • Date filtering works across year boundaries, such as from=2023-11&to=2024-02.
  • Invalid formats like YYYY-MM-DD return 400 Bad Request.
  • Filtering by a non-existent engagement ID returns an empty result set.

Retrieving a payroll change

Use this endpoint to get details for a specific payroll change.

GET /v1/payroll_changes/:id

Path parameters

ParameterTypeRequiredDescription
idstringYesPayroll change ID in strong ID format

Example request

GET /v1/payroll_changes/psc_xyz789

Example response

{
  "data": {
    "id": "psc_xyz789",
    "type": "BONUS",
    "engagement": {
      "id": "eng_abc123",
      "name": "John Smith",
      "type": "EMPLOYMENT"
    },
    "amount": {
      "decimal": "500.00",
      "currencyCode": "USD"
    },
    "month": "3",
    "year": "2024",
    "notes": "Q1 performance bonus",
    "state": "PLANNED"
  }
}

Common error

StatusMeaning
404 Not FoundPayroll change does not exist or does not belong to your company

Creating a payroll change

Use this endpoint to create a one-time payroll adjustment.

POST /v1/payroll_changes

Request body

FieldTypeRequiredDescription
engagementIdstringYesEngagement ID in strong ID format
typestringYesPayroll change type
amountobjectYesAmount object
amount.decimalstringYesPositive decimal string, such as "150.30"
amount.currencyCodestringYesISO 4217 currency code matching the engagement currency
monthstringYesMonth as a string, "1" to "12". A leading zero is accepted, such as "03". Responses return it unpadded
yearstringYesYear as a string, such as "2026". Must be between "2020" and "2040"
notesstringNoOptional description, max 2,000 characters

Example request

POST /v1/payroll_changes
{
  "engagementId": "eng_abc123",
  "type": "BONUS",
  "amount": {
    "decimal": "500.00",
    "currencyCode": "USD"
  },
  "month": "3",
  "year": "2024",
  "notes": "Q1 performance bonus"
}

Example response

{
  "data": {
    "id": "psc_xyz789",
    "type": "BONUS",
    "engagement": {
      "id": "eng_abc123",
      "name": "John Smith",
      "type": "EMPLOYMENT"
    },
    "amount": {
      "decimal": "500.00",
      "currencyCode": "USD"
    },
    "month": "3",
    "year": "2024",
    "notes": "Q1 performance bonus",
    "state": "PLANNED"
  }
}

Creation validation rules

Before creating a payroll change, confirm:

  • The amount is positive.
  • The amount currency matches the engagement’s salary currency.
  • The month and year are in the current or a future open payroll period.
  • The selected month is not before the engagement start date.
  • The selected month is not after the engagement termination date, if the engagement is terminated.
  • The payroll period is not locked.
  • The engagement exists and belongs to your company.
  • The engagement is active or has a future start date.
  • The engagement is not a pay-as-you-go contractor.
  • The request does not duplicate a recently created payroll change. A create is rejected as a duplicate when the engagement, type, year, month, amount, currency, and notes all match a payroll change created within the previous 30 seconds. Canceled payroll changes are included in this check.

Common creation errors

StatusCauseExample
400 Bad RequestInvalid request format or parametersInvalid amount format
422 Unprocessable EntityBusiness rule validation failurePayroll period is locked

Example 400 response:

{
  "errors": [
    {
      "field": "amount.decimal",
      "message": "Amount decimal is in invalid format"
    }
  ]
}

Example 422 response:

{
  "errors": [
    {
      "message": "Payroll is locked for selected month. Please schedule payroll change for the future."
    }
  ]
}

Updating a payroll change

Use this endpoint to update the amount of an existing payroll change.

PATCH /v1/payroll_changes/:id

Important restrictions

  • Only the amount can be updated.
  • To change the type, month, year, or engagement, delete the payroll change and create a new one.
  • The currency code must match the engagement’s currency.
  • The amount must be positive.
  • Canceled payroll changes cannot be updated.

Request body

FieldTypeRequiredDescription
amountobjectYesUpdated amount object
amount.decimalstringYesNew positive decimal amount
amount.currencyCodestringYesISO 4217 currency code

Example request

PATCH /v1/payroll_changes/psc_xyz789
{
  "amount": {
    "decimal": "750.00",
    "currencyCode": "USD"
  }
}

Example response

{
  "data": {
    "id": "psc_xyz789",
    "type": "BONUS",
    "engagement": {
      "id": "eng_abc123",
      "name": "John Smith",
      "type": "EMPLOYMENT"
    },
    "amount": {
      "decimal": "750.00",
      "currencyCode": "USD"
    },
    "month": "3",
    "year": "2024",
    "notes": "Q1 performance bonus",
    "state": "PLANNED"
  }
}

Canceling or deleting a payroll change

There are two ways to cancel a payroll change.

Option A: Cancel with a reason (Preferred)

Use this endpoint when you want to provide a specific cancellation reason. It currently exists only on /v0.1, which is a live, supported API version. There is no /v1 equivalent, so calling /v0.1 for this operation alongside /v1 for everything else is expected.

POST /v0.1/payroll_changes/:id/cancel

Request body:

{
  "reason": "Incorrect amount - will recreate with correct value"
}

Successful response:

200 OK

The response body is empty.

Option B: Delete the payroll change

Use this endpoint for a convenience cancellation with a default reason.

DELETE /v1/payroll_changes/:id

This is equivalent to canceling with:

{
  "reason": "Cancelled via API"
}

Successful response:

200 OK

The response body is empty.

Cancellation restrictions

  • Only payroll changes in PLANNED or APPLIED state can be canceled.
  • Already canceled payroll changes cannot be canceled again.
  • Cancellation is permanent and cannot be undone.
  • Cutoff date validation may apply.



Did this page help you?