Managing EOR employment agreements

This guide will show you how to manage EOR employment agreements for employments using the Oyster API. The different agreements are:

Agreement nameTypeRequired?Description
Oyster Service agreementsOYSTER_SERVICE_AGREEMENTYesThe agreement between Oyster and the customer for each employment. If the agreement type is AUTOMATED (see Retrieve service agreement), it is verified by Oyster staff and then accepted via API (see step 3). If it is MANUAL, it is sent for e-signature like the other agreements and can't be accepted via API.
Employment AgreementEMPLOYMENT_AGREEMENTYesThe employment agreement between Oyster and the employee.
Remote Work ScheduleREMOTE_WORKYesThis agreement is between Oyster and the employee.
Confidentiality and Intellectual Property AgreementCONFIDENTIALITY_AND_INTELLECTUAL_PROPERTYNoThis agreement is included by default for direct customers, but not for reseller end clients. It can be enabled or disabled using the Manage Confidentiality and Intellectual Property Agreement endpoint while the employment is in draft, before it is submitted.
Restrictive Covenant AgreementRESTRICTIVE_COVENANTNoThis agreement is optional. It can be added when creating the EOR employment using the Manage Restrictive Covenant Agreement endpoint while the employment is in draft, before it is submitted. It is only available in some countries. Use the Rulesets for updating employment endpoint to check availability and the allowed options for a country (see restrictiveCovenants in the response).

All the above agreements are generated when the employment is submitted, except a MANUAL OYSTER_SERVICE_AGREEMENT, which remains PENDING until Oyster uploads it. The other agreements are then regenerated with the employee's details once onboarding is completed. The other agreements can only be verified once the onboarding form has been completed and the OYSTER_SERVICE_AGREEMENT has been accepted (automated) or signed (manual). This can be done using the Creating and submitting an EOR employment and Completing EOR employment onboarding guides.

Note for Reseller partners using the API - you will need to use the X-Oyster-Customer-Id header for all of the calls in this guide. For more information, see the Reseller guide to using the Oyster API.


1. Retrieve agreements for an employment

Before you can interact with individual agreements, you first need to retrieve a list of all agreements associated with a specific employment using the Retrieve all agreements for employment endpoint.

Example Request

curl --request GET \
     --url https://api.oysterhr.com/v0.1/hiring/employments/urY7zKOm/agreements \
     --header 'accept: application/json' \
     --header 'authorization: Bearer BEARER_TOKEN_GOES_HERE'

Response

The response will contain a list of agreements associated with the employment, including agreement IDs that will be needed in the next steps. The response will also include the agreement state, its parties (who signs it: COMPANY and/or TEAM_MEMBER), and a preview with the document url. A preview is not available for agreements in the PENDING state.

Note: agreement ids are specific to the agreement version. If an agreement is changed, the id will become deprecated and a new id will be generated for the same agreement type.

Example Response

{
  "data": [
    {
      "id": "Secik2BS",
      "name": "Confidentiality and Intellectual Property Agreement",
      "type": "CONFIDENTIALITY_AND_INTELLECTUAL_PROPERTY",
      "parties": ["TEAM_MEMBER", "COMPANY"],
      "state": "READY_TO_SIGN",
      "preview": {
        "url": "http://api.example.com/files/disk/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdDVG9JYTJWNVNTSWhjblJ0TUdSaGFHbGtlR3gyYm5FMk0zbGxaVE4wTVd3MWEzQjRlUVk2QmtWVU9oQmthWE53YjNOcGRHbHZia2tpQVlCcGJteHBibVU3SUdacGJHVnVZVzFsUFNKamIyNW1hV1JsYm5ScFlXeHBkSGxmWVc1a1gybHVkR1ZzYkdWamRIVmhiRjl3Y205d1pYSjBlUzV3WkdZaU95Qm1hV3hsYm1GdFpTbzlWVlJHTFRnbkoyTnZibVpwWkdWdWRHbGhiR2wwZVY5aGJtUmZhVzUwWld4c1pXTjBkV0ZzWDNCeWIzQmxjblI1TG5Ca1pnWTdCbFE2RVdOdmJuUmxiblJmZEhsd1pVa2lGR0Z3Y0d4cFkyRjBhVzl1TDNCa1pnWTdCbFE2RVhObGNuWnBZMlZmYm1GdFpUb0pkR1Z6ZEE9PSIsImV4cCI6IjIwMjItMTAtMTVUMTI6Mzk6NTYuMDAwWiIsInB1ciI6ImJsb2Jfa2V5In19--891285f78fcdbeda014119c9851c46d187886c7f/confidentiality_and_intellectual_property.pdf",
        "contentType": "application/pdf"
      }
    },
    {
      "id": "yfp7SoKW",
      "name": "Employment Agreement",
      "type": "EMPLOYMENT_AGREEMENT",
      "parties": ["TEAM_MEMBER"],
      "state": "DRAFT",
      "preview": {
        "url": "http://api.example.com/files/disk/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdDVG9JYTJWNVNTSWhlV3R1Y1RnNU5tOTRaVFpoWmpCME1tODJNVFJpWkRoc2EySmtNUVk2QmtWVU9oQmthWE53YjNOcGRHbHZia2tpVzJsdWJHbHVaVHNnWm1sc1pXNWhiV1U5SW1WdGNHeHZlVzFsYm5SZllXZHlaV1Z0Wlc1MExuQmtaaUk3SUdacGJHVnVZVzFsS2oxVlZFWXRPQ2NuWlcxd2JHOTViV1Z1ZEY5aFozSmxaVzFsYm5RdWNHUm1CanNHVkRvUlkyOXVkR1Z1ZEY5MGVYQmxTU0lVWVhCd2JHbGpZWFJwYjI0dmNHUm1CanNHVkRvUmMyVnlkbWxqWlY5dVlXMWxPZ2wwWlhOMCIsImV4cCI6IjIwMjItMTAtMTVUMTI6Mzk6NTYuMDAwWiIsInB1ciI6ImJsb2Jfa2V5In19--5f6c5bcb7bc9c2a83314b7cd4e88f7f0c4d94d4b/employment_agreement.pdf",
        "contentType": "application/pdf"
      }
    },
    {
      "id": "zAol69ON",
      "name": "Oyster Service Agreement",
      "type": "OYSTER_SERVICE_AGREEMENT",
      "parties": ["COMPANY"],
      "state": "SIGNED",
      "preview": {
        "url": "http://api.example.com/files/disk/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdDVG9JYTJWNVNTSWhZbWswTUdOM2RURXplVEpqZEdGMU5XazFkMk55ZG5SdVpqWnNjUVk2QmtWVU9oQmthWE53YjNOcGRHbHZia2tpWTJsdWJHbHVaVHNnWm1sc1pXNWhiV1U5SW05NWMzUmxjbDl6WlhKMmFXTmxYMkZuY21WbGJXVnVkQzV3WkdZaU95Qm1hV3hsYm1GdFpTbzlWVlJHTFRnbkoyOTVjM1JsY2w5elpYSjJhV05sWDJGbmNtVmxiV1Z1ZEM1d1pHWUdPd1pVT2hGamIyNTBaVzUwWDNSNWNHVkpJaFJoY0hCc2FXTmhkR2x2Ymk5d1pHWUdPd1pVT2hGelpYSjJhV05sWDI1aGJXVTZDWFJsYzNRPSIsImV4cCI6IjIwMjItMTAtMTVUMTI6Mzk6NTYuMDAwWiIsInB1ciI6ImJsb2Jfa2V5In19--1a55a5eda382483c2a0a6ebad6e6cf8e846e2851/oyster_service_agreement.pdf",
        "contentType": "application/pdf"
      }
    },
    {
      "id": "HMNcgIHy",
      "name": "Remote Work Schedule",
      "type": "REMOTE_WORK",
      "parties": ["TEAM_MEMBER"],
      "state": "DRAFT",
      "preview": {
        "url": "http://api.example.com/files/disk/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdDVG9JYTJWNVNTSWhObXRzYW1jM2REVm5OV2h6TVhNMGVHMXJjV2g2WjNabE5YRm1jQVk2QmtWVU9oQmthWE53YjNOcGRHbHZia2tpU1dsdWJHbHVaVHNnWm1sc1pXNWhiV1U5SW5KbGJXOTBaVjkzYjNKckxuQmtaaUk3SUdacGJHVnVZVzFsS2oxVlZFWXRPQ2NuY21WdGIzUmxYM2R2Y21zdWNHUm1CanNHVkRvUlkyOXVkR1Z1ZEY5MGVYQmxTU0lVWVhCd2JHbGpZWFJwYjI0dmNHUm1CanNHVkRvUmMyVnlkbWxqWlY5dVlXMWxPZ2wwWlhOMCIsImV4cCI6IjIwMjItMTAtMTVUMTI6Mzk6NTYuMDAwWiIsInB1ciI6ImJsb2Jfa2V5In19--aa45396c236570287976bf45ddd809af74b79506/remote_work.pdf",
        "contentType": "application/pdf"
      }
    },
    {
      "id": "1L4sIgzU",
      "name": "Restrictive Covenant Agreement",
      "type": "RESTRICTIVE_COVENANT",
      "parties": ["TEAM_MEMBER", "COMPANY"],
      "state": "PENDING"
    }
  ],
  "meta": {
    "items": 100,
    "count": 5,
    "page": 1,
    "pages": 1,
    "firstUrl": "/v0.1/hiring/employments/IcPSdGdh/agreements?perPage=100&page=1",
    "lastUrl": "/v0.1/hiring/employments/IcPSdGdh/agreements?perPage=100&page=1",
    "pageUrl": "/v0.1/hiring/employments/IcPSdGdh/agreements?perPage=100&page=1",
    "nextUrl": "/v0.1/hiring/employments/IcPSdGdh/agreements?perPage=100&page=",
    "prevUrl": "/v0.1/hiring/employments/IcPSdGdh/agreements?perPage=100&page="
  }
}

Once you have the list of agreements, you can also retrieve the details of a specific agreement by using its agreement_id using the Retrieve an individual agreement endpoint.

Example Request

curl --request GET \
     --url https://api.oysterhr.com/v0.1/hiring/employments/urY7zKOm/agreements/Z1tadFBB \
     --header 'accept: application/json' \
     --header 'authorization: Bearer BEARER_TOKEN_GOES_HERE'

Example Response

{
  "data": {
    "id": "Z1tadFBB",
    "name": "Employment Agreement",
    "type": "EMPLOYMENT_AGREEMENT",
    "parties": ["TEAM_MEMBER"],
    "state": "DRAFT",
    "preview": {
      "url": "http://api.example.com/files/disk/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdDVG9JYTJWNVNTSWhNSFJyYTNoaFl6Y3dabXBoZW5CdGFtTjRNR1Y1WmpscU56TjNOd1k2QmtWVU9oQmthWE53YjNOcGRHbHZia2tpVzJsdWJHbHVaVHNnWm1sc1pXNWhiV1U5SW1WdGNHeHZlVzFsYm5SZllXZHlaV1Z0Wlc1MExuQmtaaUk3SUdacGJHVnVZVzFsS2oxVlZFWXRPQ2NuWlcxd2JHOTViV1Z1ZEY5aFozSmxaVzFsYm5RdWNHUm1CanNHVkRvUlkyOXVkR1Z1ZEY5MGVYQmxTU0lVWVhCd2JHbGpZWFJwYjI0dmNHUm1CanNHVkRvUmMyVnlkbWxqWlY5dVlXMWxPZ2wwWlhOMCIsImV4cCI6IjIwMjItMTAtMTVUMTI6Mzk6NTYuMDAwWiIsInB1ciI6ImJsb2Jfa2V5In19--f7179cbad211970d9f4fd4883ad4d8d6a564da35/employment_agreement.pdf",
      "contentType": "application/pdf"
    }
  }
}

2. Retrieve the Oyster Service Agreement

Use the Retrieve service agreement endpoint to check the type and state of the Oyster Service Agreement. The type determines the next step:

  • AUTOMATED: once Oyster staff have verified it (state of VERIFIED), accept it using the accept endpoint (see step 3).
  • MANUAL: it remains PENDING until Oyster uploads it. Once it is in the DRAFT state, verify it using the Verify agreements endpoint (see step 5).

For an AUTOMATED service agreement, the response also includes the subscription and costs details. Before the service agreement is accepted, preview contains a draft version of the document. This draft does not include the final costs, so use the subscription and costs fields to review the agreement before accepting it. Once the service agreement is accepted, the final document is generated in the background and replaces the draft in preview. Until generation finishes, preview may still return the draft.

Example Request

curl --request GET \
     --url https://api.oysterhr.com/v0.1/employments/urY7zKOm/service_agreement \
     --header 'accept: application/json' \
     --header 'authorization: Bearer BEARER_TOKEN_GOES_HERE'

Example Response (automated service agreement)

{
  "data": {
    "id": "A83ca69e",
    "type": "AUTOMATED",
    "state": "VERIFIED",
    "preview": {
      "url": "http://api.example.com/files/disk/.../oyster_service_agreement.pdf",
      "contentType": "application/pdf"
    },
    "subscription": {
      "name": "Standard Subscription",
      "billingFrequencyInMonths": 12,
      "oysterFees": [
        {
          "amount": { "decimal": "2.00", "currencyCode": "USD" },
          "startsOn": "2022-10-15",
          "endsOn": "2023-10-15",
          "discountPercentage": "10.0"
        }
      ]
    },
    "costs": {
      "administrativeCost": { "decimal": "5.00", "currencyCode": "EUR" },
      "benefitsCost": { "decimal": "12.00", "currencyCode": "EUR" },
      "benefitsSupportFee": { "decimal": "3.00", "currencyCode": "USD" },
      "serviceFee": { "decimal": "10.00", "currencyCode": "EUR" },
      "totalBenefitsCost": { "decimal": "15.00", "currencyCode": "EUR" },
      "totalCost": { "decimal": "20.00", "currencyCode": "USD" }
    }
  }
}

Example Response (manual service agreement)

{
  "data": {
    "id": "A83ca69e",
    "type": "MANUAL",
    "state": "PENDING",
    "preview": null
  }
}

3. Accept the Oyster Service Agreement (AUTOMATED only)

If the Oyster Service Agreement type is AUTOMATED, it is not sent out for signature; instead it is accepted via API using the Accept service agreement endpoint. It must be accepted before any of the other agreements can be verified (see step 5).

  • The service agreement can only be accepted once it has been verified by Oyster staff (state of VERIFIED). Calling the accept endpoint before the service agreement has been verified will return an error.
  • This endpoint requires the X-Oyster-Actor-Email header when using client credentials authentication.

Example Request

curl --request POST \
     --url https://api.oysterhr.com/v0.1/employments/urY7zKOm/service_agreement/accept \
     --header 'accept: application/json' \
     --header 'authorization: Bearer BEARER_TOKEN_GOES_HERE' \
     --header 'X-Oyster-Actor-Email: [email protected]'

Response

A successful request will return the service agreement with a state of ACCEPTED and the details of who accepted it.

Example Response

{
  "data": {
    "state": "ACCEPTED",
    "acceptedBy": {
      "name": "Jane Doe",
      "email": "[email protected]",
      "at": "2022-10-15T12:34:56.000Z",
      "method": "API"
    }
  }
}

Example Error Response (service agreement not yet verified)

{
  "errors": [
    {
      "message": "Service agreement is pending verification"
    }
  ]
}

Example Error Response (manual service agreement)

{
  "errors": [
    {
      "message": "Operation is not permitted"
    }
  ]
}

4. Complete the onboarding form

Once the OYSTER_SERVICE_AGREEMENT has been accepted (automated) or signed (manual), complete the employee's onboarding form by following the Completing EOR employment onboarding guide. The onboarding form must be completed before any of the other agreements can be verified (see step 5).

If the OYSTER_SERVICE_AGREEMENT has not been accepted or signed yet, an error response will be returned:

{
  "errors": [
    {
      "message": "Onboarding cannot be completed until the Oyster Service Agreement has been finalized"
    }
  ]
}

5. Verify Agreements and send for signature

You can then use the Verify agreements endpoint, which will verify the agreements so they can be sent out for signature. Please note that if you are a Partner collecting signatures via API, some exceptions to this section apply outlined in section 6.2 below.

Note: if the OYSTER_SERVICE_AGREEMENT type is AUTOMATED, it is verified by Oyster staff only, and an error response will be returned if you try to verify it with this endpoint. Once it has been verified, accept it as described in step 3. If the type is MANUAL, it remains PENDING until Oyster uploads it. Once it is in the DRAFT state, verify it with this endpoint. If the response shows a state of READY_TO_SIGN, it has been sent to the customer for signature (see step 6).

The other agreements can only be verified once the onboarding form has been completed and the OYSTER_SERVICE_AGREEMENT has been accepted (automated) or signed (manual). Otherwise an error response will be returned:

{
  "errors": [
    {
      "message": "The Oyster Service Agreement must be finalized before verifying any other agreement."
    }
  ]
}

Note: agreement ids are specific to the agreement version. If an agreement is changed, the id will become deprecated and a new id will be generated for the same agreement type. An error response will be returned if verifying an obsolete version.

Example Request

curl --request POST \
     --url https://api.oysterhr.com/v0.1/hiring/employments/urY7zKOm/agreements/Z1tadFBB/verify \
     --header 'accept: application/json' \
     --header 'authorization: Bearer BEARER_TOKEN_GOES_HERE'

Response

A successful request will return the agreement data with a state of VERIFIED.

Example Response

{
  "data": {
    "id": "Z1tadFBB",
    "name": "Employment Agreement",
    "type": "EMPLOYMENT_AGREEMENT",
    "parties": ["TEAM_MEMBER"],
    "state": "VERIFIED",
    "preview": {
      "url": "http://api.example.com/files/disk/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdDVG9JYTJWNVNTSWhkbmxyWWpsb2QzQnlkekpwZWpaMmREbHBPV2c0ZDJaMmFqRnpkQVk2QmtWVU9oQmthWE53YjNOcGRHbHZia2tpVzJsdWJHbHVaVHNnWm1sc1pXNWhiV1U5SW1WdGNHeHZlVzFsYm5SZllXZHlaV1Z0Wlc1MExuQmtaaUk3SUdacGJHVnVZVzFsS2oxVlZFWXRPQ2NuWlcxd2JHOTViV1Z1ZEY5aFozSmxaVzFsYm5RdWNHUm1CanNHVkRvUlkyOXVkR1Z1ZEY5MGVYQmxTU0lVWVhCd2JHbGpZWFJwYjI0dmNHUm1CanNHVkRvUmMyVnlkbWxqWlY5dVlXMWxPZ2wwWlhOMCIsImV4cCI6IjIwMjItMTAtMTVUMTI6Mzk6NTYuMDAwWiIsInB1ciI6ImJsb2Jfa2V5In19--aaa53e2cbd817ba14c6fb06270d373dcc6be3f24/employment_agreement.pdf",
      "contentType": "application/pdf"
    }
  }
}

6. Sending agreements for signature

How agreements are signed depends on your account. Most integrations follow the standard flow (6.1). Approved partners with access to the agreement signatures API collect the team member's signature themselves (6.2).

6.1 Standard flow

Agreements are sent for signature automatically, without any additional API call, once the criteria below are fulfilled. Accepting or signing the OYSTER_SERVICE_AGREEMENT or completing onboarding does not trigger sending on its own. The other agreements are sent when you verify the last remaining agreement in step 5.

  • OYSTER_SERVICE_AGREEMENT - If the type is AUTOMATED, this agreement is not sent for signature. It is accepted via API once verified by Oyster staff (see step 3), and the final document is generated in the background on acceptance. If the type is MANUAL and e-signature is enabled for it, it is sent to the customer for signature as soon as it is verified. Otherwise, it remains VERIFIED while Oyster collects the signed copy outside the API.
  • The other employment agreements (EMPLOYMENT_AGREEMENT and the REMOTE_WORK, CONFIDENTIALITY_AND_INTELLECTUAL_PROPERTY, and RESTRICTIVE_COVENANT as applicable) will be sent for signature to the respective parties (customer and/or employee) once
    • All available agreements have been verified
    • The OYSTER_SERVICE_AGREEMENT has been accepted (automated) or signed (manual)
    • The onboarding form has been completed (see step 4)
    • The employment country is listed in the list of eSignature countries in the Hiring API country coverage guide.

Agreements without e-signature (for example, in countries not listed as eSignature countries in the Hiring API country coverage guide) are not included in the signature request. They remain in the VERIFIED state while Oyster collects the signed copy outside the API, and move to SIGNED once it has been uploaded. You can track this using the Retrieve agreements endpoint in step 1.

6.2 Collecting signatures via API (Reseller partners only)

Note: access to the agreement signatures API is restricted. Please reach out to [email protected] so we can evaluate your eligibility.

If your account has access to the agreement signatures API, you collect the team member's signature for EMPLOYMENT_AGREEMENT and REMOTE_WORK in your own product and send it to Oyster using the Collect agreement signature endpoint. For your account, the EMPLOYMENT_AGREEMENT and REMOTE_WORK agreements are generated in the VERIFIED state, so they don't need to be verified in step 5.

  • Use the latest agreement id from the Retrieve agreements endpoint (step 1). If the agreement has changed since you retrieved it, the request will return an error.
  • This endpoint requires the X-Oyster-Actor-Email header when using client credentials authentication.

Example Request

curl --request POST \
     --url https://api.oysterhr.com/v0.1/hiring/employments/urY7zKOm/agreements/Z1tadFBB/signatures \
     --header 'accept: application/json' \
     --header 'content-type: application/json' \
     --header 'authorization: Bearer BEARER_TOKEN_GOES_HERE' \
     --header 'X-Oyster-Actor-Email: [email protected]' \
     --data '{ "signatoryName": "John Doe" }'

Response

A successful request will return the agreement with a state of READY_TO_SIGN. Oyster then completes the signing process, and the agreement moves to SIGNED. You can track this using the Retrieve agreements endpoint (step 1).

Example Response

{
  "data": {
    "id": "Z1tadFBB",
    "name": "Employment Agreement",
    "type": "EMPLOYMENT_AGREEMENT",
    "parties": ["TEAM_MEMBER"],
    "state": "READY_TO_SIGN",
    "preview": {
      "url": "http://api.example.com/files/disk/.../employment_agreement.pdf",
      "contentType": "application/pdf"
    }
  }
}

Example Error Responses

{
  "errors": [
    {
      "message": "Only employment agreement and remote work agreements can be signed."
    }
  ]
}
{
  "errors": [
    {
      "message": "Only verified agreements can be signed."
    }
  ]
}
{
  "errors": [
    {
      "message": "The agreement has new changes. Only the newest version can be signed."
    }
  ]
}
{
  "errors": [
    {
      "message": "Agreement already has a collected signature."
    }
  ]
}

Note: if the OYSTER_SERVICE_AGREEMENT type is MANUAL, it is not sent for e-signature after verification. It remains VERIFIED while Oyster collects the signed copy outside the API.


Conclusion

Once all employment agreements have been signed, the employment will be ready to start work on the start date. From this point on, any additional employment related changes will need to be handled using the contract changes API.


Did this page help you?