Amendment Lifecycle

Create a draft amendment

Create a new draft amendment by specifying the contract_id. You can include all, some, or none data points you plan to amend. This allows you to start with a basic amendment and add more details later.

$curl --location -g --request POST '{{host}}/rest/eor/contracts/{{contract_id}}/amendments' \
>--header 'Authorization: Bearer {{token}}' \
>--header 'Content-Type: application/json' \
>--data-raw '{
> "data": {
> }
>}'

OR

$curl --location -g --request POST '{{host}}/rest/eor/contracts/{{contract_id}}/amendments' \
>--header 'Authorization: Bearer {{token}}' \
>--header 'Content-Type: application/json' \
>--data-raw '{
> "data": {
> "time_off_type": "SPECIFIC",
> "holidays": 12,
> "employment_type": "PART_TIME"
> }
>}'

Update existing draft amendment

Update an existing draft amendment by including the amendment_id returned when the amendment was created. You can add additional data points, modify existing ones, or set the effective date. This endpoint allows you to build your amendment incrementally.

Updating an amendment replaces its full set of data points. Each update request must contain all the data points you want to keep, not just the one you’re changing. If you only send the newly updated data point, any previously saved data points that are not included will be removed from the amendment.
$curl --location -g --request PATCH '{{host}}/rest/eor/contracts/{{contract_id}}/amendments/{{amendment_id}}' \
>--header 'Authorization: Bearer {{token}}' \
>--header 'Content-Type: application/json' \
>--data-raw '{
> "data": {
> "time_off_type": "SPECIFIC",
> "holidays": 12,
> "employment_type": "PART_TIME",
> "effective_date": "2025-08-01",
> }
>}'

Amendment validation approaches

The amendment submission endpoint always validates the request, so pre-validation is optional. You’d add it when you want to give the user inline feedback before they submit, or when a field is gated by AI or country-specific rules that can’t be reproduced in the browser.

Where you validate a field depends on its external_validation flag in the amendment settings endpoint response. When external_validation: false, the field is a simple enum or numeric bound that the settings endpoint already describes, so you can validate it client-side for immediate feedback. When external_validation: true, the field goes through AI job-scope checks, job categorization, or country-specific rules that only the validate amendment endpoint can evaluate, so it requires a server-side call.

For production integrations, combine the two: validate external_validation: false fields client-side using the rules from the amendment settings endpoint, and validate external_validation: true fields server-side by calling the validate amendment endpoint. This gives you immediate feedback for cheap validations while still running the checks that only the server can do; the server response also returns the AI job scope check and job categorization results when applicable.

Validate amendment data points

Use the validate amendment endpoint to validate amendment data points before submission.

$curl --location --request POST '{{host}}/rest/eor/contracts/{{contract_id}}/amendments/validate' \
>--header 'Authorization: Bearer {{token}}' \
>--header 'Content-Type: application/json' \
>--data-raw '{
> "data": {
> "job_title": "Senior Software Engineer",
> "scope": "Lead development of web applications"
> }
>}'

Response format

The response includes validation results with field-specific error mapping:

1{
2 "data": {
3 "is_valid": true,
4 "validation_error": [],
5 "disabled_amendments": [],
6 "ai_job_scope_and_title_check": {
7 "is_valid": true,
8 "ai_scope_check_public_id": "9f9e086f-2195-4ced-a641-c62d5cd388fc"
9 },
10 "job_categorization": {
11 "is_valid": true,
12 "job_categorization_log_id": "366681cd-fe16-4be7-9e45-692a4bbc91c0",
13 "job_category": "Software Engineer",
14 "job_code": "214129"
15 }
16 }
17}

Error response with field mapping

When validation fails, errors include a field parameter for easy frontend integration:

1{
2 "data": {
3 "is_valid": false,
4 "validation_error": [
5 {
6 "code": "AMENDMENT_ITEM_VALIDATION_FAILED",
7 "message": "Scope exceeds maximum length of 20,000 characters",
8 "field": "scope",
9 "details": {
10 "newValue": "Very long scope description...",
11 "previousValue": "Short scope"
12 }
13 }
14 ],
15 "disabled_amendments": [],
16 "ai_job_scope_and_title_check": null,
17 "job_categorization": null
18 }
19}

Seniority ID

To amend seniority_id:

1

Retrieve seniority levels

Use the Get Seniority List endpoint to get available levels.

2

Include in the amendment request

Add the selected seniority_id to your amendment request body.

Effective date limitations

1

Create an amendment

Use the Create Amendment endpoint.

2

Fetch effective date limits

Call the Get Effective Date Limitations endpoint.

3

Set the date in your UI

Use the returned rules to configure the date picker or apply the default.

4

Submit with effective date (optional)

Include the effective date in the amendment request if required.

AI job scope check and job categorization

AI review check

When you create or update a job title or scope amendment, Deel automatically runs an AI review check to confirm the new title and scope are consistent with the existing job description and responsibilities. To avoid surprises at submission time, call the validate amendment endpoint first so you can inspect the AI check status before proceeding.

A successful validation returns an ai_scope_check_public_id. Include this ID in the amendment request body so the AI check result carries over and the amendment skips manual review. You can still submit without pre-validating, but if the AI check then fails, the amendment is flagged for manual review by Deel’s internal team before it can proceed.

1

Validate job title and scope

Use the validate amendment endpoint:

1{
2 "url": "https://api.letsdeel.com/rest/eor/contracts/{{contract_id}}/amendments/validate",
3 "method": "POST",
4 "headers": {
5 "accept": "application/json",
6 "Content-Type": "application/json",
7 "Authorization": "Bearer {API_TOKEN}"
8 },
9 "body": {
10 "data": {
11 "job_title": "Senior Software Manager"
12 }
13 }
14}

Response:

1{
2 "data": {
3 "is_valid": true,
4 "validation_error": [],
5 "disabled_amendments": [],
6 "ai_job_scope_and_title_check": {
7 "is_valid": true,
8 "ai_scope_check_public_id": "9f9e086f-2195-4ced-a641-c62d5cd388fc"
9 },
10 "job_categorization": null
11 }
12}
2

Submit with the AI scope check ID

Include ai_scope_check_public_id from the validation response:

1{
2 "url": "https://api.letsdeel.com/rest/eor/contracts/{{contract_id}}/amendments/{{amendment_id}}",
3 "method": "PATCH",
4 "headers": {
5 "accept": "application/json",
6 "Content-Type": "application/json",
7 "Authorization": "Bearer {API_TOKEN}"
8 },
9 "body": {
10 "job_title": "Senior Software Manager",
11 "additional_info": {
12 "ai_scope_check_public_id": "9f9e086f-2195-4ced-a641-c62d5cd388fc"
13 }
14 }
15}

Job categorization with AI recommendations

Job categorization is only available for specific countries. The following table lists where it is supported and what the AI generates per country:

CountryAI Generates
BulgariaJob code and category
LatviaJob code and category
RomaniaJob code and category
UkraineJob category
  • Every time a job title is amended, an AI job categorization is triggered to classify the new job title into a predefined job category and code.
  • You can trigger the validate amendment endpoint for the AI job categorization to be performed before creating or updating the job title amendment.
  • You can submit the amendment regardless of the AI job categorization outcome, but if the AI job categorization fails, the amendment will be flagged for manual review by Deel’s internal team.
  • You will get job_categorization_log_id when using validate amendment endpoint if the AI job categorization succeeds, which can be used to track the review status.
  • You will also get job_code and job_category along with job_categorization_log_id in the response if the AI job categorization succeeds.
  • You need to send this job_categorization_log_id and job_code and job_category in create or update amendment request so it won’t require any manual review.
1

Validate job title

Use the validate amendment endpoint:

1{
2 "url": "https://api.letsdeel.com/rest/eor/contracts/{{contract_id}}/amendments/validate",
3 "method": "POST",
4 "headers": {
5 "accept": "application/json",
6 "Content-Type": "application/json",
7 "Authorization": "Bearer {API_TOKEN}"
8 },
9 "body": {
10 "data": {
11 "job_title": "Senior Software Manager"
12 }
13 }
14}

Response:

1{
2 "data": {
3 "is_valid": true,
4 "validation_error": [],
5 "disabled_amendments": [],
6 "ai_job_scope_and_title_check": {
7 "is_valid": false,
8 "message": "Job scope is too short"
9 },
10 "job_categorization": {
11 "is_valid": true,
12 "job_categorization_log_id": "366681cd-fe16-4be7-9e45-692a4bbc91c0",
13 "job_category": "Manager, Software Development",
14 "job_code": "13306011"
15 }
16 }
17}
2

Submit with the job categorization ID

Include job_categorization_log_id, job_code, and job_category from the validation response:

1{
2 "url": "https://api.letsdeel.com/rest/eor/contracts/{{contract_id}}/amendments/{{amendment_id}}",
3 "method": "PATCH",
4 "headers": {
5 "accept": "application/json",
6 "Content-Type": "application/json",
7 "Authorization": "Bearer {API_TOKEN}"
8 },
9 "body": {
10 "job_title": "Senior Software Manager",
11 "additional_info": {
12 "job_categorization_log_id": "366681cd-fe16-4be7-9e45-692a4bbc91c0"
13 },
14 "job_category": "Manager, Software Development",
15 "job_code": "13306011"
16 }
17}

When you trigger the validate amendment endpoint for job title amendments:

  • In countries where job categorization is supported:
    • Both AI check and job categorization run.
    • You must send ai_scope_check_public_id and job_categorization_log_id together with job_code and job_category in the update amendment request to avoid manual review.
  • In countries where job categorization is not supported:
    • Only AI check runs.

Update amendment with both ai_scope_check_public_id and job_categorization_log_id

Use this request to update an amendment with both the AI check and job categorization identifiers:

1{
2 "url": "https://api.letsdeel.com/rest/eor/contracts/{{contract_id}}/amendments/{{amendment_id}}",
3 "method": "PATCH",
4 "headers": {
5 "accept": "application/json",
6 "Content-Type": "application/json",
7 "Authorization": "Bearer {API_TOKEN}"
8 },
9 "body": {
10 "job_title": "Senior Software Manager",
11 "additional_info": {
12 "ai_scope_check_public_id": "9f9e086f-2195-4ced-a641-c62d5cd388fc",
13 "job_categorization_log_id": "366681cd-fe16-4be7-9e45-692a4bbc91c0"
14 },
15 "job_category": "Manager, Software Development",
16 "job_code": "13306011"
17 }
18}

Validation errors and disabled amendments

Amendment requests can fail if validation rules are not met. In these cases, the API returns an error response that includes details about validation errors and disabled data points.

Standard error format

Error responses follow a consistent format that is easy to interpret and integrate. Every error includes a code and a message. The optional field attribute identifies which input caused the error when applicable, and an optional details object carries additional context.

Mandatory fields:

FieldTypeDescriptionExample
errorsarrayArray of error objects[{...}]
errors[].codestringError code identifier"VALIDATION_ERROR"
errors[].messagestringHuman-readable error message"Internal validation error occurred"

Optional fields

FieldTypeDescriptionExample
errors[].fieldstringField name that caused the error"effective_date"
errors[].detailsobjectAdditional error context in free-form object structure{"previousValue": "96000", "newValue": "3000"}

Common amendment error codes

Error CodeDescriptionHTTP Status
VALIDATION_ERROR, AMENDMENT_ERRORRequest validation failed400
NOT_FOUND, CONTRACT_NOT_FOUND, AMENDMENT_NOT_FOUNDResource not found404
AMENDMENT_CREATION_FAILED, AMENDMENT_UPDATE_FAILED, AMENDMENT_ITEM_DISABLED, AMENDMENT_ITEM_VALIDATION_FAILEDField validation failed422
FORBIDDENInsufficient permissions403
CHANGE_REQUEST_CONFIRMED, RESOURCE_CONFLICTResource conflict409
INTERNAL_ERRORServer error500

Error response examples

The following examples show the different types of validation and amendment errors you may encounter.

Returned when the request payload fails general validation checks.

1{
2 "errors": [
3 {
4 "code": "VALIDATION_ERROR",
5 "message": "Internal validation error occurred"
6 }
7 ]
8}

Returned when the amendment request cannot proceed due to one or more issues.

1{
2 "errors": [
3 {
4 "message": "Amendment already in progress with ID: 5ea6c745-3f40-4fc8-864e-026ccae666e1",
5 "code": "AMENDMENT_ERROR"
6 }
7 ]
8}

Returned when specific fields are invalid or disabled for the requested amendment.

1{
2 "errors": [
3 {
4 "message": "Job title is not enabled for white label API",
5 "code": "AMENDMENT_ITEM_DISABLED",
6 "field": "jobTitle"
7 },
8 {
9 "message": "Minimum salary for United Kingdom is $30,312.07",
10 "code": "AMENDMENT_ITEM_VALIDATION_FAILED",
11 "field": "salary",
12 "details": {
13 "previousValue": "96000.0000",
14 "newValue": "3000"
15 }
16 }
17 ]
18}

Returned when an amendment cannot be updated or confirmed.

1{
2 "errors": [
3 {
4 "code": "AMENDMENT_UPDATE_FAILED",
5 "message": "Failed to update amendment",
6 "field": "amendment"
7 }
8 ]
9}
1{
2 "errors": [
3 {
4 "code": "CHANGE_REQUEST_CONFIRMED",
5 "message": "Change request is already confirmed",
6 "field": "changeRequestId"
7 }
8 ]
9}

Confirm an amendment

To confirm a draft amendment, use the contract_id and amendment_id. Only draft amendment can be confirmed.

After confirmation, the flow depends on the amendment type. If the type is instant, the amendment becomes active immediately. If it requires signatures, Deel or the employee must approve it first.

$curl --location -g --request POST '{{host}}/rest/eor/contracts/{{contract_id}}/amendments/{{amendment_id}}/confirm' \
>--header 'Authorization: Bearer {{token}}'

Cancel an amendment

To cancel an amendment, use the contract_id and amendment_id. You can cancel an amendment any time before the employee signs it. After cancellation, you can delete it if needed.

$curl --location -g --request DELETE '{{host}}/rest/eor/contracts/{{contract_id}}/amendments/{{amendment_id}}' \
>--header 'Authorization: Bearer {{token}}'

Download an amendment PDF

Retrieve the download URL for the Statement of Work (Employee Agreement, EA) of a confirmed amendment. The URL is available only after the amendment is confirmed and the contract is in progress.

The download URL expires after 15 minutes.

$curl --location -g --request GET '{{host}}/rest/eor/contracts/{{contract_id}}/amendments/{{amendment_id}}/pdf' \
>--header 'Authorization: Bearer {{token}}'

Accept an amendment

Use this endpoint to accept an amendment submitted by a Deel admin.

After acceptance, the amendment either becomes active immediately or moves to the signature step, depending on its type.

$curl --location -g --request POST '{{host}}/rest/eor/contracts/{{contract_id}}/amendments/{{amendment_id}}/accept' \
>--header 'Authorization: Bearer {{token}}'

Sign an approved amendment

An employee can sign an amendment approved by both the client and admin. After signing, the amendment becomes active.

$curl --location -g --request POST '{{host}}/rest/eor/contracts/{{contract_id}}/amendments/{{amendment_id}}/sign' \
>--header 'Authorization: Bearer {{token}}'

Retrieve the list of amendments

Retrieve all amendments for a specific contract.

$curl --location -g --request GET '{{host}}/rest/eor/contracts/{{contract_id}}/amendments' \
>--header 'Authorization: Bearer {{token}}'

Retrieve a specific amendment

Retrieve details of a specific amendment

$curl --location -g --request GET '{{host}}/rest/eor/contracts/{{contract_id}}/amendments/{{amendment_id}}' \
>--header 'Authorization: Bearer {{token}}'