Companies, employees and contracts

The client's establishments, the people employed there, and the contracts that bind them. These reads carry personal data: every field is classified in the API reference, and some are withheld unless you were granted a scope of their own.

Read Discovery first — the referentials it describes are what the identifiers below resolve against.

Companies

GET /api/v1/client/companies (scopes: companies:read) GET /api/v1/client/companies/{id} (same scopes)

{
  "items": [
    {
      "id": "lPBpV3",
      "name": "Atelier Central",
      "code": "AC-01",
      "siret": "12345678900012",
      "siren": "123456789",
      "isHeadquarter": true,
      "isManagingEstablishment": false,
      "businessSign": "Atelier",
      "address": {
        "street": "12 rue des Fabriques",
        "postalCode": "69001",
        "city": "Lyon",
        "country": "France"
      },
      "phoneNumber": "0400000000",
      "legalCategoryLabel": "SAS",
      "activityCode": "2562B",
      "activityLabel": "Mecanique industrielle",
      "collectiveAgreementName": "Metallurgie",
      "collectiveAgreementIdcc": "3248",
      "weeklyWorkingHours": 35,
      "masterCompanyId": "dV3QPr",
      "masterName": "Groupe Central"
    }
  ],
  "nextCursor": null
}
Field What it tells you
id Opaque identifier. Every other read takes it as companyId.
siret, siren Public legal identifiers, in clear — they are not opaque, and you may match on them.
isHeadquarter true for the registered office.
collectiveAgreementIdcc The IDCC code, which joins the collective-agreement referential.
masterCompanyId The parent establishment, when there is one.

The list returns one entry per establishment of your perimeter. A credential mandated on a group pages through every establishment it reaches; one mandated on a single company still holds exactly one entry. The shape of the answer is the same either way, so page through it rather than reading the first element.

Employees

GET /api/v1/client/employees (scopes: employees:read) GET /api/v1/client/employees/{id} (same scopes)

{
  "items": [
    {
      "id": "1VaKPa",
      "fullName": "Camille Dupont",
      "civility": "Mme",
      "employeeNumber": "E-0042",
      "professionalEmail": null,
      "companyId": "dV3QPr",
      "companyName": "Atelier Central",
      "isActive": true,
      "jobFunction": "Chef d\u0027atelier",
      "managerResourceId": "GNnBNO",
      "managerFullName": "Renaud Lambert"
    },
    {
      "id": "DJABPM",
      "fullName": "Camille Dupont",
      "civility": "Mme",
      "employeeNumber": "E-0042",
      "professionalEmail": null,
      "companyId": "dV3QPr",
      "companyName": "Atelier Central",
      "isActive": true,
      "jobFunction": "Chef d\u0027atelier",
      "managerResourceId": "GNnBNO",
      "managerFullName": "Renaud Lambert"
    }
  ],
  "nextCursor": null
}
Field What it tells you
id Opaque identifier — the {id} the contract and leave reads take.
employeeNumber The staff number as the employer set it. Not guaranteed unique across companies.
isActive false for someone who has left. They are still returned.
managerResourceId Their manager, when that manager's record sits in the same company.
managerFullName The manager's name, on the same condition.

A manager in another company comes back as null, not as an unresolvable id. Groups where people report across establishments will therefore see gaps in the reporting line. That is deliberate: naming someone outside the perimeter you were granted would disclose them.

Confidential and archived records are never returned, by any read. An employee marked confidential in Staff & Go is absent from this list and answers 404 by id — the same answer an identifier that does not exist gets, so you cannot tell the two apart. That is the point.

Employment contracts

GET /api/v1/client/employees/{id}/employments (scopes: employments:read) GET /api/v1/client/employments/{id} (same scopes)

{
  "id": "lPBpV3",
  "employeeId": "GNnBNO",
  "companyId": "dV3QPr",
  "startDate": "2020-01-06",
  "endDate": null,
  "contractType": "CDI",
  "jobTitle": "Chef d atelier",
  "hiringClass": "CADRE",
  "weeklyWorkingHours": 35,
  "managerResourceId": "XJ2rJL",
  "grossSalary": null,
  "netSalary": null,
  "salaryPeriod": null,
  "restricted": [
    {
      "reason": "requires_scope",
      "fields": [
        "grossSalary",
        "netSalary",
        "salaryPeriod"
      ]
    },
    {
      "reason": "requires_company_opt_in",
      "fields": [
        "grossSalary",
        "netSalary",
        "salaryPeriod"
      ]
    }
  ]
}
Field What it tells you
id The contract.
employeeId Whose it is — the identifier the employee reads take.
contractType Spelt out ("CDI"), in your language — send Accept-Language.
jobTitle The job, free text as the employer wrote it.
hiringClass The professional status as recorded. See the warning below.
weeklyWorkingHours Contractual weekly hours.
grossSalary, netSalary, salaryPeriod Pay. Behind a scope of its own — see below.

hiringClass is free text with no referential behind it. It is written by a payroll connector's sync field and by a document template, with no foreign key and nothing in Staff & Go joining it to the professional-status referential. Do not join it on hiringCode, whatever the two happen to look like: they coincide only when the connector uses the same vocabulary, and nothing enforces that.

Expect several contracts where you might expect one. An employee accumulates amendments over a career, and each is a row here. The listing is paged; page it to the end rather than reading the first entry.

An employee id that is not one of yours answers 404, not an empty page. It designates the subject of the read, and a subject that does not resolve has no list to be empty. That differs from a filter, which narrows a set and can legitimately narrow it to nothing.

Withheld fields

Some fields are returned only to a partner that was granted a scope for them. Pay is the first: employments:read:compensation, required in addition to employments:read, which never implies it.

When a field is withheld it comes back null and is named in restricted:

"grossSalary": null,
"restricted": [
  { "reason": "requires_scope", "fields": ["grossSalary", "netSalary", "salaryPeriod"] },
  { "reason": "requires_company_opt_in", "fields": ["grossSalary", "netSalary", "salaryPeriod"] }
]

A null on its own never means "withheld". A field that is null and absent from restricted really has no value recorded. The two are different facts and this contract keeps them apart — do not report one as the other to your users.

Reason What unblocks it
requires_scope Ask the client's administrator to grant the sub-scope on your credential.
requires_company_opt_in The company itself has not opened the field. Only it can.
requires_plan Your partner plan does not cover the field.

Several reasons can appear at once, and they all matter. They are settled by different parties — the administrator who provisioned your credential, the company being read, and your own plan — so clearing one leaves the others standing. A partner told only about the "first" obstacle would obtain it and come back to the very same withheld field.

restricted travels inside each entry, including in a page, never once above the list. On a listing spanning several companies, some will have opened a field and others not, so no single envelope could describe a page truthfully. When nothing was withheld the array is empty — one shape, always.

Three reads carry this envelope: employment contracts, leave lines and variable payroll elements. They are the only ones with a field that can be withheld, and each has a sub-scope of its own — employments:read:compensation, leaves:read:reason, payroll:read:amounts — as well as its own company opt-in. None of the three implies another: contractual pay and what a payroll month recorded are two separate decisions, taken separately by each company. The others — companies, employees, balances, referentials, expense reports, meal voucher counts, payslips — have no restricted key at all, and that absence says there is nothing here to withhold, never that nothing was. A null on those reads is therefore always a value that is not recorded: the manager in another company described above is one of those, not a withheld field.