Onboarding Employment

Prerequisites

Before you begin, ensure that you meet the following requirements:

Create employment

Check country availability

Before proceeding, confirm that your desired country is supported by the Remote API. You can do this by calling the list countries endpoint. The API response of this call will include a list of supported countries along with their respective country codes. These codes are required to create an employment.

ℹ️ If your desired country is not on the list of supported countries, you can reach out to your Remote contact or email us at api-support@remote.com to express your interest in hiring in that country.

Populate basic information

First, you will need to populate the employment with employee's "basic information". The Remote API requires this information for every supported country. As the required payload may cary by country, you will have to look up the expected payload.

Using JSON schema for expected payload

The Remote API uses JSON schemas to define the expected basic information payload format. You can retrieve the employment_basic_information JSON schema for your country using the show form schema endpoint. This endpoint required two parameters: country_code and form. Let's say, your country of employment is Canada, your request will look like:

$ curl --location \ --header 'Authorization: Bearer eyJraWQiO...' \ --request GET 'https://gateway.remote-sandbox.com/v1/countries/CAN/employment_basic_information'

The resulting JSON schema provides the required fields, labels, descriptions, and validation rules.

{ "data": { "additionalProperties": false, "allOf": [ { "else": { "properties": { "seniority_date": false } }, "if": { "properties": { "has_seniority_date": { "const": "yes" } }, "required": [ "has_seniority_date" ] }, "then": { "required": [ "seniority_date" ] } } ], "properties": { "department": { "properties": { "id": { "description": "(Optional) Select a department or create one.", "oneOf": [ { "const": null } ], "title": "Department", "type": [ "string", "null" ], "x-jsf-presentation": { "creatableOn": "name", "inputType": "select", "meta": "departments", "placeholder": "Search or create a department..." } }, "name": { "description": "Name of the department to be created if none is selected", "maxLength": 255, "title": "Department name", "type": [ "string", "null" ], "x-jsf-presentation": { "inputType": "text" } } }, "title": "Department", "type": [ "object", "null" ], "x-jsf-order": [ "id", "name" ], "x-jsf-presentation": { "inputType": "fieldset" } }, "email": { "description": "This is how the employee will access their account.", "format": "email", "maxLength": 255, "title": "Personal email", "type": "string", "x-jsf-presentation": { "inputType": "email" } }, "has_seniority_date": { "description": "If the employee started working for your company before being added to Remote, then select Yes.", "oneOf": [ { "const": "yes", "title": "Yes" }, { "const": "no", "title": "No" } ], "title": "Does the employee have a seniority date?", "type": "string", "x-jsf-presentation": { "direction": "row", "inputType": "radio" } }, "job_title": { "description": "We can hire most roles but there are some we cannot support. This includes licensed roles, blue collar workers, and employees with certain C-level job titles.", "maxLength": 255, "pattern": "\\S", "title": "Job title", "type": "string", "x-jsf-presentation": { "inputType": "text" } }, "manager": { "properties": { "id": { "description": "(Optional) The person who will manage this employee day-to-day on the Remote platform.", "oneOf": [ { "const": "48359c5f-ff4e-4a2c-83e9-5c6f6f22f8bb", "title": "Easha Abid", "x-jsf-presentation": { "meta": { "assigned_roles": [ { "data_scope": "all", "name": "Owner", "slug": "6541dadb-d3e0-45a0-9e32-094df0e5ce95", "type": "owner" } ] } } }, { "const": null } ], "title": "Manager", "type": [ "string", "null" ], "x-jsf-presentation": { "inputType": "select", "meta": "team_members", "placeholder": "Select a manager" } } }, "title": "Manager", "type": [ "object", "null" ], "x-jsf-order": [ "id" ], "x-jsf-presentation": { "inputType": "fieldset" } }, "name": { "description": "Full employee name as it appears on identification document.", "maxLength": 255, "pattern": "\\S", "title": "Full name", "type": "string", "x-jsf-presentation": { "inputType": "text" } }, "provisional_start_date": { "description": "The minimum onboarding time for Canada is 2 working days. We will confirm the start date once you invite the employee to do the self-enrollment. We strongly recommend a later start date if you plan to conduct background checks and want to lower termination risks and costs due to unsatisfactory results.", "format": "date", "maxLength": 255, "title": "Provisional start date", "type": "string", "x-jsf-logic-validations": [ "blocked_date_validation" ], "x-jsf-presentation": { "blockedDates": [], "inputType": "date", "meta": { "mot": 2 }, "minDate": "2025-03-12", "softBlockedDates": [] } }, "seniority_date": { "description": "Please indicate if different from contract start date", "format": "date", "title": "Seniority date", "type": [ "string", "null" ], "x-jsf-presentation": { "inputType": "date" } }, "work_email": { "description": "The employee's company email. For example, jane@company.com.", "format": "email", "maxLength": 255, "title": "Work email", "type": "string", "x-jsf-presentation": { "inputType": "email" } } }, "required": [ "name", "email", "job_title", "provisional_start_date", "has_seniority_date" ], "type": "object", "x-jsf-logic": { "validations": { "blocked_date_validation": { "errorMessage": "Date is blocked due to the holiday season, and the limited availability of internal/external providers to process onboarding, payroll and benefits enrollments", "rule": { "!": { "in": [ { "var": "provisional_start_date" }, [] ] } } } } }, "x-jsf-order": [ "name", "email", "work_email", "job_title", "department", "provisional_start_date", "has_seniority_date", "seniority_date", "manager" ] } }

⚠️ Since employment data schemes are dynamic, we highly recommend dynamically generating UI forms when using the Remote API in production. Learn more from How JSON Schemas Work guide.

Basic information constraints

  1. Understandingprovisional_start_date validations
    While JSON schemas define validation rules for most fields, provisional_start_date includes additional complex rules that are only enforced by the API when calling the create employment endpoint. Here's an example schema snippet:
    { "title": "Provisional start date", "format": "date", "x-jsf-presentation": { "inputType": "date", "meta": { "mot": 3 }, "minDate": "2025-03-10" } }
    Key validation rules include:
    1. Minimum onboarding time (MOT): Each country has a minimum number of working days required to onboard an employee before their start date. The meta.mot field defines this in the schema. For example, if today is March 10 and the MOT is 3 days, then the selected date needs to be March 13 or after. This is included in the JSON Schema above.
    2. Weekends: Some countries do not allow start dates on weekends, which may differ globally.
    3. Holidays: National holidays may restrict start dates.
    4. Arbitrary dates: Certain days (e.g., Christmas Eve) may be disallowed.
      The Remote API enforces these rules and if a request violates any of them, an error response will explain the issue.
  2. Validation flow
    The onboarding process may take several days. You can use meta.mot to implement an expiration system that notifies customers if a selected start date becomes invalid. For example, let's say the MOT is 3 days, today is Monday and they selected the start date for Friday. The days have passed and now it’s Thursday but the customer hasn’t finished the flow yet. The start date is no longer valid, so through meta.mot, you can automatically notify your customer about it.

Create employment

After retrieving the JSON schema, submit the data using the create employment endpoint. The employment you create will be associated with the company-scoped access token of the company for which you are setting up the employment. Send the required fields as a JSON-encoded payload inside the basic_information object.

{ "employment": { "basic_information": { "email": "jane@smith.com", "has_seniority_date": "no", "job_title": "Engineer", "name": "Jane Smith", "provisional_start_date": "2022-07-10" }, "company_id": "20a72f86-company-id-20a72f86", "country_code": "AUS", "created_at": "2023-02-01T15:42:03", "employment_lifecycle_stage": "employment_creation", "full_name": "Jane Smith", "id": "663e0b79-c893-45ff-a1b2-f6dcabc098b5", "job_title": "Engineer", "personal_email": "jane@smith.com", "provisional_start_date": "2022-07-10", "type": "employee", "updated_at": "2023-02-01T15:42:03" } }

In this request,

  • basic_information: includes an employee's basic data. The basic_information object may include additional dynamic fields based on the country, such as has_seniority_date. Because these fields vary per country, using JSON schemas is essential to ensure each country's specific data requirements are met.
  • company_id: corresponds to the unique company ID to which the employee needs to be added.
  • country_code: indicates the country where the employee is located.
  • created_at: marks the timestamp at which the new employment was created.
  • employment_lifecycle_stage: (Refer to the detailed section below).
  • provisional_start_date: refers to the official start date of the new employment.
  • type: indicates the type of employment being created. The employment can be an employee or a contractor.

    ℹ️ For the employment types contractor, global_payroll_employee and direct_employee, only List employments and Show employment operations are available.

  • updated_at: refers to the timestamp at which the employment was last updated.

🧪 Sandbox testing:

To test employment creation in a sandbox environment, use the Create employment endpoint. It allows you to create an employment while bypassing the provisional_start_date validation, which means you can add a date that's in the past as well.

Employment lifecycle stage

Below is a list of possible stages in the employment lifecycle, represented by the employment_lifecycle_stage field in the Employment resource of the Remote API.

  • employment_creation: This is the initial stage when required fields are still missing. Employment data can be submitted once using Create employmentand later completed via Update employment. Once all required information is provided, the stage automatically advances to employment_self_enrollment.

  • employment_self_enrollment: At this stage, the employee or contractor must log in to the Remote platform and complete the necessary forms, which vary by country. Some information can be pre-filled with the Update employment endpoint. Refer to the API specification for details.

  • right_to_work_check: Remote verifies the employee's or contractor's work eligibility for the specified country. After passing this check, Remote will facilitate contract signing. There's no need for Remote API at this stage.

  • contract_signing: Once self-enrolment (and, the right-to-work check, if applicable) is complete, the employee remains in this stage until both parties sign the Employment Agreement Contract. This process takes place outside of the Remote API with Remote assisting as needed.

  • remote_enrollment: After contract signing is done, Remote handles benefits enrolment, payroll setup, and other administrative steps. No API action is required at this time. This is the final step before employment begins.

  • onboarded: The employee is officially ready to start work.

  • offboarded: The employment is archived, and the employee is offboarded.

    The transition to the offboarded stage takes time, as it requires both the employment to be archived and the termination_date to be reached. Here are some edge cases:

    • Employee initiated resignation afterprovisional_start_date :
      If the employee resigns through Remote's platform and their termination_date is after the provisional_start_date, the employment_lifecycle_stage remains onboarded until the termination_date is reached. Once this termination_date passes, the employment status changes to archived, and the lifecycle stage is updated to offboarded.
    • Termination or resignation beforeprovisional_start_date :
      If the employee is terminated or resigns before their provisional_start_date and has not started onboarding, Remote will delete the employee without triggering any webhooks. After deletion, API calls to the Employments endpoints will return a 404 response.

ℹ️ The employment_creation is the only stage that requires data to be sent through the API. The other stages will move forward automatically based on the events that happen in the Remote Platform or taken by any party — company, employment, or Remote.

When an employment is created, it goes through different stages. Let's take a look at what an employment body looks like after creation:

{ "employment": { "address_details": {}, "administrative_details": {}, "bank_account_details": [], "basic_information": {}, "billing_address_details": {}, "company_id": "e31adae1-company-id-af5fba7dd803", "contract_details": {}, "country": { ... }, "created_at": "2021-11-11T18:44:39", "eligible_for_onboarding_cancellation": true, "emergency_contact_details": {}, "files": [], "full_name": "Jane Smith", "id": "20a72f86-employment-id-9e4942a902ff", "job_title": "Engineer", "manager": "John Doe", "manager_email": "john.doe@company.com", "manager_employment_id": "20a72f86-employment-id-9e4942a902ff", "onboarding_tasks": { ... }, "provisional_start_date": "2021-07-03", "status": "created", "type": "employee", "updated_at": "2021-11-11T18:44:39", "user_status": "active", "work_email": "jane.smith@company.com" } }

Here are some new fields that you will notice in this definition:

  • status:
    Here is a list of all possible values represented by status in the employment API.
    • created: Indicates that the employment has been created. At this stage, employer can edit employee's data.
    • created_awaiting_reserve: Indicates that the employment has been created but Remote is waiting for a reserve payment before an employer can invite the employee.
    • created_reserve_paid: Indicates that the reserve payment has been paid by the employer.
    • invited: Indicates that the employee has been invited but didn't start the onboarding process. At this stage, employer can no longer make changes to the employee's contract details.
    • initiated: Indicates that the employee has started their onboarding.
      On completion of every onboarding task, the employment.onboarding_task.completed event is triggered.
    • review: Indicates that the employee has completed onboarding and are being reviewed by the Remote admin.
    • pending: Indicates that employment is missing necessary fields while onboarding.
    • active: Indicates that the employment is active. The status is set to active when all user tasks are completed and the user status is also active. (Learn more about user statuses in the section below).
      On active, the employment.onboarding.completed event is triggered.
    • archived: Indicates that the employment has been archived and no longer in use. When an employment is marked as archived, the user status changes to inactive.
    • deleted: Indicates that the employment has been soft deleted by the admin. Accounts are deleted at the request of the users. When this is done, the user will no longer appear on the team page.
  • user_status:
    • created: Indicates that a user was creates but did not start the onboarding.
    • initiated: Indicates that a user has started their onboarding process.
    • active: Indicates that a user has completed onboarding. This status is achieved when all onboarding user tasks are completed and the corresponding employment has a status review, pending, active, or archived.
      The employment.user_status.activated webhook is triggered when a user status changes to active.
    • cancelled: Indicates that a user contract was canceled. At this stage, the user can still log in and access their data and documents.
    • deleted: Indicates that a user has been deleted and can no longer log into Remote platform. Deleted users do not show up on the people page but can be searched in the Users table by a Remote admin.
    • inactive: Indicates that a user was deactivated and their employment is suspended. At this stage, the user can still log into the platform to access their data and documents but their invoicing capabilities have been revoked. The employer can reactivate a user at any time.
      The employment.user_status.deactivated webhook is triggered every time a user status changes to inactive.

ℹ️ If your company-scoped access token has expired, you'll receive an Unauthorized response. To obtain a new valid token, follow the refresh token flow.

The employment_lifecycle_stage field tracks the employment's progress through different stages. Learn more from the employment lifecycle stages section.

🧪 Sandbox testing:

When providing an email address in sandbox environment, it is recommended to use a unique, valid email that can receive messages. This allows you to verify that the employee is receiving emails as expected.

If you're using Gmail, GSuite, or Outlook, you can create unique email addresses by appending +randomtext to your existing address. For example, if your email is bob@example.com, you can use bob+1@example.com. This ensures uniqueness while still delivering emails to the same inbox.

Now that we have an employment, we can get started with its onboarding.

Update employment

When an employment record is created, its initial employment_lifecycle_stage is employment_creation. To move to the next stage, you need to update the employment record. Employment data can only be updated through the Remote API before an invitation is sent to the employee using the Update employment endpoint.

Add / modify employment information

An already created employment, that is not deleted can be updated at any time. Through this endpoint, you can modify all information submitted during the creation step and add additional details like home address, bank details, emergency contact, etc. But once the onboarding process is complete, only a limited set of employment details such as emergency_contact_details can be modified. So, if you need to make changes to the employment record, make sure to do so before sending the invite.

ℹ️ Updates are blocked once the invitation has been sent because in certain cases, employees need to agree to the amendments made, for example, changes to their contract_details, etc.

To make the required edits, simply make a patch request by passing the employment_id. When updating an employment, the following events are triggered depending on what change was made to the employment.

A few important points to keep in mind while using this endpoint:

  • For created employment, you can update all basic parameters, onboarding tasks, as well as basic_information.
  • For active employment, you can update manager information, emergency contact details, and address details using this endpoint.
  • Before modifying country-specific data, we recommend viewing the JSON form schema because a country's compliance requirements change frequently, which results in a change in required and returned fields.
  • If you are using this endpoint to build an integration, make sure to dynamically collect the latest parameters for each country by calling the show form schema endpoint.
  • Updating contractors' details via API is not yet supported. Please reach out to your Remote contact for assistance.

🧪 Sandbox testing:

In a sandbox environment, you can call the Update employment endpoint that allows you to edit the employment status of an employee.

For example, here's how you can use this endpoint to add contract and pricing details to the employment. For this call, you need to provide:

  • employment_id: This is the id returned when you created the employment.
  • contract_details: This field contains employment contract information, which varies by country. To obtain the necessary fields for a specific country, you can query the show form schema endpoint, passing both country_code and contract_details as path parameters.
  • pricing_plan_details: To obtain the necessary fields, you can query the show form schema endpoint by passing the country_code and pricing_plan_details as the form type.

Here's what an example request will look like:

'{ "contract_details": { "available_pto": 33, "annual_gross_salary": 100000, "benefits": { "employee_assistance_program": "no", "health": "Basic - Employee Only (Canada Life - Basic Health Employee Only; Canada Life - Basic Dental Employee Only; Canada Life - Basic Vision; Canada Life - Basic Life; Canada Life - Basic AD&D)", "retirement": "Basic Retirement (Canada Life - Basic Retirement)" }, "bonus_details": "every year", "commissions_details": "15% every year", "contract_duration": "Permanent", "company_business_description": "We are a consulting company, helping businesses implement the best APIs out there.", "experience_level": "Level 3 - Associate - Employees who perform independently tasks and/or with coordination and control functions", "equity_compensation": { "offer_equity_compensation": "no" }, "has_bonus": "yes", "has_commissions": "yes", "has_signing_bonus": "yes", "probation_length": 3, "role_description": "Manage a quickly growing and business-critical team. Contribute to hiring and retaining product designers; help to shape the team culture.", "province_of_residency": "AB", "signing_bonus_amount": 15000, "supervisor_name": "Sally", "work_address_is_home_address": "yes" }, "pricing_plan_details": { "frequency": "annually" } }'

Invite employee

Once all the required data has been sent, you can proceed with inviting the employee using the invite employment endpoint. On a successful call, the employee will receive an email from no-reply@remote-sandbox.com. Upon receiving that email, the employee will begin their self-enrolment process, where they provide any missing details, such as administrative information, home address, and other necessary data.

Show employment

After the invitation has been sent, if you want to check the status of the employment, you can call the show employment endpoint and focus on employment_lifecycle_stage and status values.

{ "data": { "employment": { ..., "employment_lifecycle_stage": "employee_self_enrollment", ..., "id": "9fb23136-bb7c-488a-b5dc-37d3b7c9033b", ... "status": "invited", } } }

Congratulations! You have successfully created and invited a new employee. Next up, you can set employment benefits for the created employment.


Did this page help you?