Découvrir ce que vous pouvez appeler
Deux lectures vous disent ce que vos identifiants autorisent et ce que la société cliente possède réellement, avant le premier appel d'écriture : votre profil et les référentiels.
Les libellés reviennent dans votre langue
Envoyez Accept-Language et tout libellé lisible de la réponse est rendu dans cette langue — types de
contrat, noms de types de congés, et le texte d'un refus. Langues gérées : de-DE, en-US, es-ES,
fr-FR, it-IT, pl-PL, pt-PT, tr-TR. Toute autre valeur retombe sur la langue par défaut plutôt
que d'échouer : un libellé dans la mauvaise langue est un moindre mal qu'une absence de réponse.
Rien de ce sur quoi vous branchez ne change. Les codes — code d'un type de congé, hiringCode d'un
statut professionnel, codeIdcc d'une convention — sont identiques dans toutes les langues, et c'est sur
eux que votre intégration s'appuie. Seul ce que vous affichez bouge.
Les identifiants sont opaques — et pas uniques d'un type à l'autre
Tout identifiant servi par cette API — un salarié, un contrat, un établissement, une ligne de congé, un bulletin — est une chaîne opaque. Stockez-la et renvoyez-la telle quelle : n'en construisez pas, n'en analysez pas le contenu, et ne supposez pas que deux d'entre eux se suivent.
La même chaîne peut être un identifiant valide dans plusieurs lectures. Un seul encodage sert toute
l'API et rien dans la chaîne ne dit ce qu'elle désigne : une valeur qui se résout en contrat sur
GET /api/v1/client/employments/{id} peut se résoudre en même temps en ligne de congé sur
GET /api/v1/client/leave-requests/{id} — deux enregistrements sans rapport. Aucune des deux réponses ne le
signale, puisque l'identifiant ne porte aucun type.
Ce qu'il faut en faire côté stockage : conservez chaque identifiant avec la lecture dont il provient,
jamais dans une table unique d'« ids », et ne rapprochez jamais des identifiants de natures différentes.
Passer un identifiant à la mauvaise lecture n'est pas une erreur que cette API peut détecter pour vous :
elle répond 404, ou elle répond un enregistrement qui n'a rien à voir avec celui que vous visiez, et les
deux ressemblent à une réponse ordinaire.
Votre profil
GET /api/v2/client/partner/profile (scopes : partner:read)
{
"partnerId": "partner-sample",
"tenantIds": [
"PB56eJ"
],
"grantedScopes": [
"external-api",
"partner:read",
"payroll:read",
"payroll:trigger",
"referentials:read",
"companies:read",
"employees:read",
"employments:read",
"leaves:read",
"beta:access"
],
"plan": "Standard",
"modules": [
{
"moduleId": 1,
"enabled": true,
"advanced": false
}
]
}
| Champ | Ce qu'il vous dit |
|---|---|
partnerId, tenantIds |
L'identité résolue par l'API depuis votre jeton, et les établissements clients sur lesquels vos appels agissent. Un identifiant peut être mandaté sur plusieurs ; jusqu'où il descend sous chacun n'est pas publié ici. Les lectures répondent sur tout le périmètre : une liste couvre tous les établissements qu'il atteint, et un {id} appartenant à l'un d'eux se résout. Un établissement qui n'a pas le module dont une lecture dépend est absent de cette lecture, sans être nommé — mais la réponse dit qu'elle est écourtée, via le bloc partial décrit plus bas. |
grantedScopes |
Les scopes réellement présents sur le jeton — le moyen le plus rapide de diagnostiquer un 403. |
plan |
Votre palier de quota. Voir la réserve ci-dessous. |
modules |
État de chaque module Staff & Go sur l'ensemble de votre périmètre : enabled dès qu'un établissement qu'il atteint le détient, plus advanced pour le second niveau, indépendant, que certaines fonctionnalités exigent. Il ne dit pas quel établissement détient quoi. |
plan n'est pas encore un palier commercial. Aujourd'hui tous les identifiants renvoient Standard,
parce que le provisioning ne le renseigne pas : traitez-le comme un emplacement réservé, pas comme le plan
que vous avez souscrit. Vos valeurs de quota viennent de votre interlocuteur Staff & Go et des en-têtes
RateLimit, pas de ce champ. Voir Limites de débit.
Un module non activé fait refuser les appels des capacités qui en dépendent : lire modules d'abord vous
épargne un refus. La correspondance entre module et capacité n'est pas encore publiée — demandez-la à votre
interlocuteur Staff & Go d'ici là.
GET /api/v1/client/partner/profile reste disponible : il renvoie partnerId, tenantIds et scopes
(renommé grantedScopes en v2), et aucun des champs ci-dessus. Voir
Versionnement.
tenantId est devenu tenantIds, une liste, sur les deux versions, du jour où un identifiant a pu
être mandaté sur plusieurs établissements. C'est un changement cassant, assumé sur v1 plutôt que différé :
une valeur unique ne peut pas décrire un tel identifiant, et en publier une aurait été faux, pas seulement
incomplet.
Quand un champ n'a pas pu être déterminé
Un champ que l'API n'a pas pu lire revient à null et est nommé dans unavailable, avec le motif :
{
"modules": null,
"unavailable": [
{ "reason": "source_not_configured", "fields": ["modules"] }
]
}
reason |
Conduite attendue |
|---|---|
source_not_configured |
La lecture en amont n'est pas branchée sur cet environnement. Réessayer donnera la même réponse — contactez-nous. |
source_unreachable |
Elle a échoué en sortie. Réessayez plus tard. |
unavailable est absent quand tout a été déterminé. Un null seul ne veut jamais dire « indéterminé » :
"modules": [] signifie qu'aucun module n'est activé pour la société, et c'est une réponse complète.
Quand une liste est plus courte que votre mandat
Vos lectures répondent sur tout le périmètre, mais elles ne servent que les établissements qui détiennent le module dont elles dépendent. Les autres sont absents, et la réponse le dit — sinon une liste écourtée serait indistinguable d'une liste honnêtement vide, alors que les deux appellent la conduite inverse :
{
"items": [],
"nextCursor": null,
"partial": { "reasons": ["module_not_subscribed"] }
}
Valeur de reasons |
Conduite attendue |
|---|---|
module_not_subscribed |
Une partie de vos établissements ne détient pas le module dont cette lecture dépend. Ni un scope ni une offre ne les débloque : c'est la société cliente qui décide, et c'est à elle qu'il faut le demander. |
unspecified |
La réponse est écourtée et l'API ne sait pas dire pourquoi. Traitez-la comme n'importe quelle autre raison d'aller regarder ce que votre mandat couvre. Rien à réessayer. |
partial est absent quand la réponse couvre tout votre périmètre — une réponse complète est donc exactement
ce qu'elle était avant que ce bloc existe.
Trois précisions qui évitent les contresens :
- Ce n'est pas
nextCursor. Le curseur dit si d'autres lignes suivent ;partialdit que des lignes manquent et ne suivront pas, si loin que vous le dérouliez. - Ce n'est pas
restricted. Celui-là concerne des champs retirés d'un enregistrement que vous avez reçu, et il voyage dans chaque entrée.partialconcerne des enregistrements que vous n'avez pas reçus du tout. - Il ne nomme jamais l'établissement manquant. Le dire reviendrait à vous apprendre quelles sociétés de votre client souscrivent quel module. Ce que vous pouvez en faire est là : la réponse est courte, et pourquoi.
Types de congés
GET /api/v1/client/referentials/leave-types (scopes : referentials:read)
{
"items": [
{
"id": 1,
"code": "SAMPLE_LEAVE",
"name": "Sample leave",
"isCustom": false,
"companyId": null
},
{
"id": 2,
"code": "SAMPLE_LEAVE",
"name": "Sample leave (company)",
"isCustom": true,
"companyId": "1VaKPa"
}
]
}
Référencez le type par son id, branchez sur son code — name est un libellé que la société peut
surcharger : s'y fier casse le jour où quelqu'un le renomme dans Staff & Go.
Un code n'est pas unique dans la réponse. Une société peut définir son propre type par-dessus le type
standard dont elle hérite, et les deux portent le même code — comme dans l'exemple ci-dessus. Indexer la
réponse par code perd donc une entrée silencieusement ; c'est isCustom (true pour le type propre à la
société) qui distingue les deux. Utilisez id s'il vous faut une clé unique.
companyId nomme l'établissement qui a défini le type, et vaut null sur un type standard — la clé est
toujours là, comme name. Ce null ne signifie pas un établissement inconnu : il signifie que personne ne
l'a défini, que tous les établissements de votre périmètre héritent de la même entrée, et qu'elle est
renvoyée une fois. Quand deux établissements définissent chacun leur type sous un même code, c'est
companyId qui les distingue.
Tout le périmètre revient en une seule réponse : cet endpoint n'offre encore ni filtre ni pagination.
Types de contrat
GET /api/v1/client/referentials/contract-types (scopes : referentials:read)
{
"items": [
{
"id": 1,
"name": "Sample contract type",
"isActive": true
},
{
"id": 2,
"name": "Sample retired contract type",
"isActive": false
}
]
}
| Champ | Ce qu'il vous dit |
|---|---|
id |
La seule ancre sûre de ce référentiel. |
name |
Un libellé, à afficher. Ne branchez jamais dessus. |
isActive |
false pour un type qu'on ne peut plus choisir sur un nouveau contrat. |
Ce référentiel n'a pas de code fonctionnel. Contrairement aux types de congés, Staff & Go n'en stocke
aucun pour un type de contrat : id est donc la seule clé sur laquelle s'appuyer, et name ne sert qu'à
l'affichage.
Les types retirés sont renvoyés, avec isActive à false. Un contrat signé il y a des années peut en
référencer un, et l'omettre vous laisserait avec un identifiant que vous ne sauriez pas résoudre. Ne prenez
donc pas l'absence d'un type dans un sélecteur pour une absence de cette liste.
C'est un référentiel de la plateforme, pas de la société : la même liste répond à tous les partenaires. Elle revient entière, sans filtre ni pagination.
Statuts professionnels
GET /api/v1/client/referentials/professional-statuses (scopes : referentials:read)
{
"items": [
{
"id": 1,
"code": "SAMPLE_STATUS",
"hiringCode": "SMP",
"name": "Sample professional status",
"isActive": true
}
]
}
| Champ | Ce qu'il vous dit |
|---|---|
id |
Identifiant technique. |
code |
Le code fonctionnel stable — c'est lui qu'on branche. Ce référentiel en a un, contrairement aux types de contrat. |
hiringCode |
La clé de rapprochement avec les conventions collectives. Voir la section suivante. |
name |
Libellé, à afficher. |
isActive |
false pour un statut retiré, renvoyé quand même. |
Référentiel de la plateforme, comme les types de contrat, et renvoyé entier.
Conventions collectives
GET /api/v1/client/referentials/collective-agreements (scopes : referentials:read)
{
"items": [
{
"id": 10,
"companyId": "1VaKPa",
"codeIdcc": "0000",
"codeCcn": null,
"codeJo": null,
"name": "Sample collective agreement",
"professionalStatusHiringCode": "SMP",
"isActive": true,
"isAgreementActive": true
},
{
"id": 10,
"companyId": "1VaKPa",
"codeIdcc": "0000",
"codeCcn": null,
"codeJo": null,
"name": "Sample collective agreement",
"professionalStatusHiringCode": null,
"isActive": true,
"isAgreementActive": true
},
{
"id": 10,
"companyId": "DJABPM",
"codeIdcc": "0000",
"codeCcn": null,
"codeJo": null,
"name": "Sample collective agreement",
"professionalStatusHiringCode": "SMP",
"isActive": false,
"isAgreementActive": true
}
]
}
Contrairement aux deux précédents, celui-ci porte la donnée de la société : la réponse diffère d'un partenaire à l'autre.
| Champ | Ce qu'il vous dit |
|---|---|
id |
Identifiant de la convention. Ce n'est pas une clé de la réponse — voir ci-dessous. |
companyId |
L'établissement qui l'applique — l'identifiant opaque servi par /companies. |
codeIdcc |
Le code IDCC, identifiant national de la convention. |
codeCcn, codeJo |
Codes complémentaires, absents quand ils ne sont pas renseignés. |
name |
Libellé de la convention. |
professionalStatusHiringCode |
Le statut professionnel couvert par ce rattachement. null signifie qu'il les couvre tous. |
isActive |
Cet établissement applique-t-il encore ce rattachement. |
isAgreementActive |
La convention existe-t-elle encore au niveau national. |
Une entrée est un rattachement, pas une convention. Une société peut appliquer des conventions
différentes à des statuts professionnels différents : deux entrées peuvent donc partager le même id — comme dans l'exemple ci-dessus — et
c'est le triplet (companyId, id, professionalStatusHiringCode) qui en identifie une. Indexer la réponse
par id perd des entrées silencieusement — c'est le même piège que les codes de types de congés, sur une
autre clé.
Si votre périmètre couvre plusieurs établissements, vous recevez une entrée par établissement. Deux
d'entre eux appliquant la même convention au même statut professionnel, ce sont deux entrées, distinguées par
companyId, chacune avec son propre isActive : un établissement peut avoir cessé d'appliquer une
convention que l'autre applique encore. isActive parle de l'établissement nommé à côté, jamais de votre
périmètre entier.
Le rapprochement avec les statuts professionnels se fait sur hiringCode, jamais sur id. Les deux
référentiels vivent dans des tables reliées un-à-plusieurs : aucune ligne de professional-statuses ne
correspond une-pour-une à un rattachement, et joindre par id renverrait une ligne — la mauvaise — sans rien
signaler.
Les deux drapeaux répondent à deux questions indépendantes. Savoir que la société n'applique plus un rattachement ne dit rien de l'existence nationale de la convention, et réciproquement. Les rattachements inactifs sont renvoyés pour qu'un contrat plus ancien reste résolvable.
Regroupements de sociétés
GET /api/v1/client/referentials/company-groupings (scopes : referentials:read)
{
"items": [
{
"id": "kNR1P3",
"name": "Unite legale Sample",
"code": "UL-SAMPLE",
"type": "LegalUnit",
"siren": "123456789",
"companyIds": [
"1VaKPa",
"DJABPM"
]
},
{
"id": "RP85Ng",
"name": "Groupement Sample",
"code": "GRP-SAMPLE",
"type": "Custom",
"siren": null,
"companyIds": [
"1VaKPa"
]
}
]
}
La manière dont le client organise ses sociétés. Rend les regroupements dont votre périmètre fait partie, sans pagination.
| Champ | Ce qu'il vous dit |
|---|---|
id |
Identifiant opaque du regroupement. |
name, code |
Des libellés que le client peut modifier. Affichez-les ; ne branchez jamais dessus. |
type |
LegalUnit (sociétés partageant un SIREN) ou Custom (regroupement défini par le client). C'est là-dessus qu'on branche. |
siren |
Présent uniquement sur un LegalUnit, en clair — identifiant légal public. |
companyIds |
Quels établissements de votre périmètre en font partie — les identifiants servis par /companies. Jamais vide. |
Un regroupement nomme une liste d'établissements, pas un seul. Contrairement à toutes les autres lectures d'ici, il ne porte pas d'établissement d'origine : un regroupement est un ensemble auquel des sociétés appartiennent, pas une chose que l'une d'elles possède. Deux de vos établissements dans un même regroupement, c'est une entrée qui les nomme tous les deux — pas deux entrées.
Ce n'est pas un organigramme. Il ne porte ni ligne hiérarchique, ni responsable, ni salarié, et les
regroupements sont plats — il n'y a ni parent ni niveau. Pour savoir qui reporte à qui, lisez
managerResourceId sur le salarié (voir Sociétés, salariés et contrats).
Il ne dit jamais combien d'autres sociétés contient un regroupement. companyIds liste les membres que
votre mandat couvre, et s'arrête là : un regroupement couvrant des sociétés que vous ne pouvez pas lire
ressemble exactement à un regroupement qui n'en couvre pas. Ne lisez pas la taille de cette liste comme la
taille du regroupement.
Se resynchroniser
Aucune de ces lectures ne propose de synchronisation incrémentale. Il n'existe pas de filtre
modifiedSince, et il n'y en aura pas en v1 : rien, côté Staff & Go, ne porte de date de dernière
modification indexée sur ces ressources. Un filtre accepté qui renverrait tout serait pire qu'un filtre
absent, puisque vous croiriez avoir reçu un delta.
Deux conséquences pour votre intégration :
- Relisez la liste entière pour vous remettre à jour. Ces référentiels sont petits et changent rarement : c'est une lecture, pas une charge.
- Une entrée disparue est simplement absente. Il n'y a ni marqueur de suppression ni pierre tombale : la réponse dit ce qui existe, et rien d'autre. Comparez-la à votre copie locale pour déduire ce qui a disparu.
Pour les volumes qui ne tiendront pas dans une réponse — les salariés d'un grand compte, par exemple — des exports en masse sont prévus. Votre interlocuteur Staff & Go vous dira quand ils arrivent.