Set Pay Rates

The "Set Pay Rates" endpoint creates or updates pay rates for one or more users in a single bulk request.

Endpoint

PUT https://api.connecteam.com/pay-rates/v1/pay-rates

Authentication

  • API Key or OAuth 2.0
  • OAuth Scope: pay_rates.write

Upsert Behavior

This endpoint uses upsert semantics: if a pay rate already exists for the same user and effective date, it is fully replaced. Otherwise, a new entry is added to the user's pay rate history. There is no separate "create" vs "update" endpoint.

Request Body

FieldTypeRequiredDescription
payRatesByUsersarrayYesArray of user pay rate objects (max 500)
payRatesByUsers[].userIdintegerYesThe user ID
payRatesByUsers[].payRate.effectiveDatestringYesEffective date (YYYY-MM-DD)
payRatesByUsers[].payRate.rateTypestringYeshourly, monthly, or yearly
payRatesByUsers[].payRate.isDefaultRateEnabledbooleanNoEnable default rate (default: true)
payRatesByUsers[].payRate.defaultRatenumberConditionalRequired when isDefaultRateEnabled is true. Must be > 0
payRatesByUsers[].payRate.isResourceRateEnabledbooleanNoEnable resource-specific rates (default: false)
payRatesByUsers[].payRate.isApplyDefaultRateToNewResourcebooleanNoAuto-apply default rate to newly assigned resources (default: true)
payRatesByUsers[].payRate.resourcesRatesarrayNoResource rate configurations

Validation Rules

  • defaultRate is required when isDefaultRateEnabled is true. Omitting it returns 400.
  • defaultRate can be omitted when isDefaultRateEnabled is false — the response will not include the field.
  • All rate values must be > 0 — zero and negative values are rejected (400). This applies to defaultRate, resource rate, and sub-resource rate.
  • effectiveDate must be in YYYY-MM-DD format — other formats like DD-MM-YYYY are rejected.
  • rateType must be one of hourly, monthly, or yearly — invalid values (e.g. weekly) are rejected.
  • User IDs must exist in your company — non-existent user IDs return 400 with the invalid IDs listed.
  • Locked days check — if any user has approved/locked timesheet days on or after the effective date, the entire request is rejected with 409 HAS_LOCKED_DAYS.

Resource Rate Configuration

When isResourceRateEnabled is true, you can configure per-resource (job) rates:

FieldTypeRequiredDescription
resourcesRates[].resourceIdstringYesResource (job) ID
resourcesRates[].ratenumberConditionalRate amount (> 0). Not required when useDefaultRate is true
resourcesRates[].useDefaultRatebooleanNoInherit the user's default rate (default: false)
resourcesRates[].subResourcesRatesarrayNoSub-resource rate configurations
subResourcesRates[].subResourceIdstringYesSub-resource ID
subResourcesRates[].ratenumberConditionalRate amount (> 0). Not required when useParentRate is true
subResourcesRates[].useParentRatebooleanNoInherit parent resource's rate (default: false)
🚧

Important: Include ALL resources

The resourcesRates array should include all resources that need custom rates. Resources not included will not have a resource-specific rate configured.

📘

Rate Inheritance

When useDefaultRate is true on a resource, the rate field is omitted from both the request and response — the resource inherits the user's default rate. The same applies to sub-resources with useParentRate set to true.

Request Example

{
  "payRatesByUsers": [
    {
      "userId": 12345,
      "payRate": {
        "effectiveDate": "2025-03-01",
        "rateType": "hourly",
        "isDefaultRateEnabled": true,
        "defaultRate": 30.00,
        "isResourceRateEnabled": true,
        "isApplyDefaultRateToNewResource": true,
        "resourcesRates": [
          {
            "resourceId": "job_001",
            "rate": 35.00,
            "useDefaultRate": false,
            "subResourcesRates": []
          },
          {
            "resourceId": "job_002",
            "useDefaultRate": true,
            "subResourcesRates": [
              {
                "subResourceId": "sub_001",
                "useParentRate": true
              }
            ]
          }
        ]
      }
    }
  ]
}

Response

The response mirrors the GET response structure, returning the created/updated pay rates for each user:

{
  "requestId": "550e8400-e29b-41d4-a716-446655440000",
  "data": {
    "payRatesByUsers": [
      {
        "userId": 12345,
        "payRate": {
          "effectiveDate": "2025-03-01",
          "rateType": "hourly",
          "isDefaultRateEnabled": true,
          "defaultRate": 30.00,
          "isResourceRateEnabled": true,
          "isApplyDefaultRateToNewResource": true,
          "resourcesRates": [...],
          "createdBy": 99999,
          "createdAt": 1709251200
        }
      }
    ]
  }
}

Error Codes

HTTP StatusError CodeDescription
4001002Invalid request body — bad rate value (0 or negative), missing required field, invalid date format, invalid rate type
4001004User validation failed — one or more user IDs don't exist in the company
401Missing authentication
403Invalid API key or insufficient OAuth scope
409HAS_LOCKED_DAYSEffective date conflicts with approved or locked timesheet days

Pay Rates API Reference


What’s Next