GuidesAPI ReferenceChangelog
Guides

Create a milestone based contract

Learn how to create contracts where the contractor is paid based on milestones

One of the ways to hire independent contractors from the UI is using a milestone contract. This guide shows how to create the same using the API.

Milestone contracts are suited for projects with specific goals and deliverables, where the amount that an individual contractor is paid is defined within a specific milestone and not within the contract. Hence, many of the compensation details are not needed when creating the contract.

Compared to the UI process, where adding at least one milestone is required upon creation, from the API milestones must be created independently once the contract is signed and they will be linked to it. For more information on the UI process, visit How To Create A Milestone Contract On Deel on the Help Center.

👍

Configure who can submit work from the UI

Either the client, the contractor, or both can submit work on milestone contracts. Visit the FAQ section of Help Center for more information on how to configure who can submit work.

Before you begin

All contracts are created by making a POST request to the Create contract endpoint. This guide focuses on the specific details of a milestone contract and assumes that the reader is already familiar with the endpoint and the data structure. These are explained in Create a contract.

1. Fill in the generic details

Start filling the data object with the contract details located the top level of the payload.

{
  "data": {
    "country_code": "US",
    "external_id": "11001100110",
    "notice_period": 15,
    "scope_of_work": "Lorem ipsum dolor sit amet.",
    "special_clause": "Lorem ipsum dolor sit amet.",
    "start_date": "2024-08-01",
    "state_code": "IL", 
    "termination_date": "2024-08-31",
    "title": "Contract",
    "type": "payg_milestones"
    …
  }
}

Where:

NameRequiredTypeFormatDescriptionExample
start_datetruestringdateThe start date of the contract. Use the ISO-8601 short date format YYYY-MM-DD.2024-08-01
titletruestring-The name of the contract.Contract
typetruestringenumDetermines the contract type. For a milestone contract, the type must be set to payg_milestones.payg_milestones
country_codefalsestring-The code of the country of the contractor's tax residence. Leave it blank to use the country indicated by the contractor when they create their profile. Use the ISO 3166-1 alpha-2 code in capital letters for the residence of the contractor. A list of the available country codes can be found on our Help Center.US
external_idfalsestring-Can be used to link the ID of the worker from third-party system or platform.11001100110
notice_periodfalsenumber-The number of days of notice required to terminate the contract.15
scope_of_workfalsestring-Fill it with a job description or a summary of the roles and responsibilities of the worker.Lorem ipsum dolor sit amet.
special_clausefalsestring-Fill it with any special clauses to be included in the contract.Lorem ipsum dolor sit amet.
state_codefalsestring-If the country_code parameter is set to US, indicates the state code of the contract.IL
termination_datefalsestringdateThe termination date of the contract. Use the ISO-8601 short date format YYYY-MM-DD.2024-08-31

2. Fill in the client information

Each contract must be associated with a legal entity and a group, that are client-specific information. The client object allows to associate the contract being created to existing legal entities and groups using their IDs. You can retrieve the IDs from the following endpoints:

👍

New legal entities and groups can be created using the POST create legal entity and the POST create group endpoints respectively.

{
  "data": {
    …,
    "client": {
      "legal_entity": {
        "id": "00000000-0000-0000-0000-000000000000"
      },
      "team": {
        "id": "00000000-0000-0000-0000-000000000000"
      }
    }
  }
}

Where:

NameRequiredTypeFormatDescriptionExample
client.legal_entity.idtruestringUUIDThe ID of the legal entity.d3m0d3m0-d3m0-d3m0-d3m0-d3m0d3m0d3m0
client.team.idtruestringUUIDThe ID of the group.d3m0d3m0-d3m0-d3m0-d3m0-d3m0d3m0d3m0

3. Fill in the worker's personal information

The worker object contains the worker's personal information. The email must be unique in your organization. If the email already exists, the contract will be connected to the worker's existing profile.

👍

Use the worker's personal email

This way they can access their account and contract even after the contract has ended.

{
  "data": {
    …,
    "worker": {
      "expected_email": "[email protected]",
      "first_name": "John",
      "last_name": "Doe"
    }
  }
}

Where:

NameRequiredTypeFormatDescriptionExample
worker.expected_emailtruestringemailThe email of the contractor[email protected]
worker.first_nametruestring-The first name of the contractorJohn
worker.last_nametruestring-The last name of the contractorDoe

4. Fill in the job title and seniority

Optionally, a job title and a seniority level can be associated to the contract using the job_title and seniority objects. You can retrieve the job title and seniority level IDs from the following endpoints:

👍

Creating new job titles

New job titles can be created by replacing the job_title.id with job_title.name, containing the name of the new job title. For example:

    "job_title": {
      "name": "Director"
    },
{
  "data": {
    …,
    "job_title": {
      "id": "12345"
    },
    "seniority": {
      "id": "12345"
    }
  }
}

Where:

NameRequiredTypeFormatDescriptionExample
job_title.idtruenumberIDThe ID of the job title12345
seniority.idfalsenumberIDThe ID of the seniority level12345

5. Fill in the meta information

The meta object allows to adjust additional settings of the contract, such as requiring the contractor to provide additional compliance documents as per their country's local laws.

👍

We recommend requesting these documents to meet compliance obligations.

{
  "data": {
    …,
    "meta": {
      "documents_required": true
    }
  }
}

Where:

NameRequiredTypeFormatDescriptionExample
documents_requiredtrueboolean-Determines whether contractors must provide additional compliance documents. We recommend requesting these documents to meet compliance obligations.true

6. Fill in the compensation details

How much the contractor will be compensated and when can be defined in the compensation_details object.

{
  "data": {
    …,
    "compensation_details": {
      "currency_code": "USD",
      "cycle_end": 30,
      "cycle_end_type": "DAY_OF_MONTH",
      "frequency": "monthly",
      "pay_before_weekends": true,
      "payment_due_days": 5,
      "payment_due_type": "REGULAR"
    }
  }
}

Where:

NameRequiredTypeFormatDescriptionExample
compensation_details.currency_codetruestring-Indicates the currency code of the compensation following the 3-digit ISO 4217 format. Visit the Help center for a list of supported currencies.USD
compensation_details.cycle_endtruenumber-Indicates the day when the invoicing cycle ends, based on what's set in the cycle_end_type parameter. For example, use 7 for a Sunday or use 30 for the 30th of the month. If the month is shorter than the number indicated, the cycle ends on the last day of the month.30
compensation_details.cycle_end_typetruestringenumTogether with the cycle_end parameter, determines when the compensation cycle ends. Available values are:
- DAY_OF_MONTH for the calendar day of the month
- DAY_OF_WEEK for the week day in weekly payments
- DAY_OF_LAST_WEEK for the last week day of the month
DAY_OF_MONTH
compensation_details.frequencytruestringenumDetermines how often the compensation is paid to the contractor. The available values are monthly, weekly, biweekly, semimonthly, and calendar_month.monthly
compensation_details.pay_before_weekendsfalseboolean-When set to true, if the pay day falls on a weekend, the compensation payment will be anticipated to the last working day of the week.true
compensation_details.payment_due_daysfalsenumber-When the payment_due_type parameter is set to REGULAR, this parameter determines the offset of days for the payment to be due. For example, set it to 5 if you want the payment to be due 5 days after the last day of the invoicing cycle.5
compensation_details.payment_due_typefalsestringenumDetermines when the payment is due. If you select REGULAR, the payment is determined by the payment_due_days parameter. If you select WITHIN_MONTH the payment is due on the last day of the invoicing cycle.REGULAR

7. Make the API request

Now that all the contract information is set, it's time to make the API request. Here's how the request should look like.

A successful response will return a 200 status code and include the ID of the created contract and all of its information in the payload. Note down the id parameter at the top of the response, it will be needed for the next step: signing the contract.

curl --request POST \
     --url 'https://api.letsdeel.com/rest/v2/contracts' \
     --header 'authorization: Bearer TOKEN' \
     --header 'content-type: application/json' \
     --data-raw '
{
  "data": {
    "country_code": "US",
    "external_id": "11001100110",
    "notice_period": 15,
    "scope_of_work": "Lorem ipsum dolor sit amet.",
    "special_clause": "Lorem ipsum dolor sit amet.",
    "start_date": "2024-08-01",
    "state_code": "IL",
    "termination_date": "2024-08-31",
    "title": "Contract",
    "type": "payg_milestones",
    "client": {
      "legal_entity": {
        "id": "00000000-0000-0000-0000-000000000000"
      },
      "team": {
        "id": "00000000-0000-0000-0000-000000000000"
      }
    },
    "worker": {
      "expected_email": "[email protected]",
      "first_name": "John",
      "last_name": "Doe"
    },
    "job_title": {
      "id": "12345"
    },
    "seniority": {
      "id": "12345"
    },
    "meta": {
      "documents_required": true
    },
    "compensation_details": {
      "currency_code": "USD",
      "cycle_end": 30,
      "cycle_end_type": "DAY_OF_MONTH",
      "frequency": "monthly",
      "pay_before_weekends": true,
      "payment_due_days": 5,
      "payment_due_type": "REGULAR"
    }    
  }
}'
{
  "data": {
      "id": "dd333m00",
      "title": "Contract",
      "type": "ongoing_time_based",
      "status": "waiting_for_client_sign",
      "created_at": "2024-07-30T10:39:30.241Z",
      "client": {
          "team": {
              "id": 123456,
              "public_id": "00000000-0000-0000-0000-000000000000",
              "name": "Wayne Enterprise Global"
          },
          "legal_entity": {
              "id": 123456,
              "name": "Wayne Enterprise Global",
              "email": "[email protected]",
              "type": "company",
              "subtype": "general-partnership",
              "registration_number": "987654321",
              "vat_number": "123456789"
          }
      },
      "worker": {
          "expected_email": "[email protected]",
          "first_name": "John",
          "last_name": "Doe"
      },
      "invitations": {
          "client_email": "",
          "worker_email": ""
      },
      "signatures": {
          "client_signature": "",
          "client_signed_at": "",
          "worker_signature": "",
          "worker_signed_at": "",
          "signed_at": ""
      },
      "compensation_details": {
            "currency_code": "USD",
            "amount": "0",
            "scale": "",
            "frequency": "",
            "first_payment": "",
            "first_payment_date": "",
            "gross_annual_salary": "",
            "gross_signing_bonus": "",
            "gross_variable_bonus": ""
      },
      "contract_template": null,
      "employment_details": {
          "type": "ongoing_time_based",
          "days_per_week": 0,
          "hours_per_day": 0,
          "probation_period": 0,
          "paid_vacation_days": 0
      },
      "job_title": "Academic Advisor",
      "scope_of_work": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
      "seniority": {
          "id": 3,
          "name": "Senior (Individual Contributor Level 3)",
          "level": 3
      },
      "special_clause": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
      "start_date": "2024-08-13T18:30:00.000Z",
      "termination_date": "2024-08-31T18:29:59.999Z",
      "is_archived": false,
      "notice_period": 15,
      "custom_fields": [],
      "external_id": null
  }
}

What’s Next

Now that the contract has been created, it's time to sign it. To learn how, check the guide on how to sign a contract.