Référence de l'API
L'API /v1 d'Agreely est le backend que les clients TypeScript,
PHP et CLI encapsulent. Elle est petite, sans état et de
serveur à serveur. Cette page est l'orientation; la
référence /v1 interactive présente les schémas complets de
requête/réponse, rendus à partir de la spécification OpenAPI.
Authentification
Chaque appel utilise une clé d'API Bearer :
Authorization: Bearer agr_live_...
La clé seule identifie le locataire. Il n'y a ni session ni CSRF. Les clés portent des portées (scopes) :
checkautorisePOST /v1/check.issueautorise les points d'accès de demande de consentement.attestautorise les points d'accès de consentement manuel.relationshipautorise la fin d'une relation client (art. 23).- La portée
checkouissuepeut lireGET /v1/catalog. - Toute clé valide (n'importe quelle portée) peut appeler
GET /v1/whoami.
Une clé absente ou invalide donne 401; une clé valide dépourvue de la portée
requise donne 403.
Points d'accès
| Méthode | Chemin | Portée | Finalité |
|---|---|---|---|
GET |
/v1/whoami |
n'importe laquelle | Les portées que porte la clé présentée (divulgation minimale). |
POST |
/v1/check |
check |
Décision de consentement synchrone pour une cellule. |
POST |
/v1/check/batch |
check |
Décisions synchrones pour plusieurs cellules en un seul appel (plafond de 500). |
GET |
/v1/catalog |
check ou issue |
Le catalogue (catégorie, finalité) actif de l'entreprise. |
POST |
/v1/consent-requests |
issue |
Émettre une demande de consentement. |
GET |
/v1/consent-requests |
issue |
Lister les demandes, plus récentes d'abord, paginées par curseur. |
GET |
/v1/consent-requests/{id} |
issue |
Une demande par son requestId de protocole. |
POST |
/v1/consent-requests/{id}/cancel |
issue |
Annuler une demande en attente (idempotent). |
POST |
/v1/manual-consents |
attest |
Enregistrer un consentement hors ligne, attesté par l'entreprise. |
POST |
/v1/manual-consents/claim-links |
attest |
Émettre un lien de réclamation à usage unique pour le citoyen. |
POST |
/v1/manual-consents/{consentRef}/revoke |
attest |
Retrait d'un consentement manuel. |
POST |
/v1/manual-consents/{consentRef}/erase |
attest |
Effacer (destruction cryptographique) un consentement manuel. |
POST |
/v1/customers/{customerRef}/relationship/end |
relationship |
Mettre fin à une relation client (art. 23, idempotent). |
L'identité de la clé (whoami)
GET /v1/whoami confirme, côté serveur, les portées que porte réellement la clé
présentée. Toute clé valide y accède (n'importe quelle portée). La réponse est
volontairement minimale : uniquement les portées de la clé, rien d'autre, pas
d'identifiant d'entreprise, pas de nom de clé, pas de compteur.
{ "scopes": ["check", "issue"] }
Une clé absente ou invalide donne 401. Comme les autres points d'accès, il est
soumis aux limites de débit (429).
L'annulation d'une demande de consentement
POST /v1/consent-requests/{id}/cancel annule une demande en attente (la voie
« révocation avant action » côté entreprise) : elle passe à l'état terminal
revoked_before_action, si bien que le lien profond du citoyen ne mène plus à une
approbation. Portée : issue. La demande est identifiée par son requestId de
protocole et rattachée à l'entreprise de la clé (jamais à une valeur du corps).
L'appel est idempotent : une demande déjà terminale (approved, refused,
expired ou revoked_before_action) n'est pas une erreur; elle renvoie son
statut courant avec cancelled: false. Seule la transition en attente vers annulée
renvoie cancelled: true.
{ "requestId": "0x...", "status": "revoked_before_action", "cancelled": true }
Un requestId inconnu, étranger à l'entreprise, ou malformé donne 404 (jamais
une écriture inter-locataires).
La vérification : un refus est un 200
Une décision de consentement, autorisation ou refus, est toujours un 200
réussi. Le refus n'est pas une erreur.
{ "decision": "deny", "status": "revoked", "consentRef": "0x...", "assurance": "citizen_signed", "checkedAt": "2026-06-26T15:04:05Z" }
status est l'un de active (autorisation), none, revoked, expired ou
erased. consentRef est présent pour chaque statut sauf none. Le corps porte
aussi assurance, citizen_signed (signé par le citoyen) ou company_attested
(consentement manuel), présent pour chaque statut sauf none : c'est le palier
du consentement sous-jacent. Envoyez les category et purpose bruts tels
que lus par un humain; le serveur les normalise. La vérification ne lit que le
statut d'un seul enregistrement d'application indexé et ne touche jamais la chaîne
ni l'indexeur, de sorte qu'une révocation refuse dès l'appel suivant. Elle
fonctionne en refus par défaut : il n'y a pas d'autorisation sans
enregistrement positif. Une panne d'Agreely doit elle aussi être traitée comme un
refus; c'est ce que font les SDK.
La vérification par lot
POST /v1/check/batch évalue plusieurs cellules en un seul aller-retour au lieu
de N appels à /v1/check. Même portée (check), même moteur de décision, même
règle « refus par défaut » et même journal d'accès par décision (Loi 25 G3) : une
seule écriture groupée, granulaire par décision. Il est rattaché à l'entreprise de
la clé. Les category / purpose sont envoyés bruts; le serveur les normalise.
Deux formes de corps sont acceptées. La forme plate :
{ "items": [ { "customerRef": "cust_8812", "category": "Phone number", "purpose": "Billing" } ] }
Et la forme groupée (développée côté serveur en un produit client x champs) :
{ "checks": [ { "customerRef": "cust_8812", "fields": [ { "category": "Phone number", "purpose": "Billing" }, { "category": "Email address", "purpose": "Newsletter" } ] } ] }
Le lot utilise customerRef, l'appel unique utilise customerId
Détail de compatibilité à connaître : POST /v1/check nomme le champ du sujet customerId, alors que POST /v1/check/batch le nomme customerRef. Les deux désignent votre propre référence client, résolue au sein de l'entreprise de la clé. Les SDK masquent cette différence.
La réponse est un tableau decisions aligné sur les cellules soumises. Chaque
décision porte les mêmes champs qu'un /v1/check unique, plus les customerRef,
category et purpose renvoyés en écho :
{ "decisions": [
{ "customerRef": "cust_8812", "category": "Phone number", "purpose": "Billing",
"decision": "allow", "status": "active", "consentRef": "0x...",
"assurance": "citizen_signed", "checkedAt": "2026-06-26T15:04:05Z" }
] }
Comme pour l'appel unique, consentRef et assurance sont absents pour le
statut none, et une cellule sans enregistrement actif renvoie deny / none
(refus par défaut). Un lot vide donne 422, et un lot de plus de 500
cellules donne 422 (invalid_request : « Batch exceeds the 500-item cap. Split
into smaller batches. ») : découpez en lots plus petits.
Cas d'usage : protéger l'affichage d'une liste de clients
Afficher un tableau de N clients x M champs est N x M usages de renseignements personnels. Sous la Loi 25, chaque champ que vous affichez est un usage encadré par une vérification (limitation de l'usage, art. 12; responsabilité, art. 3.1). Le lot regroupe la grille entière en un seul aller-retour tout en conservant une décision et une entrée de journal d'accès par cellule. Le regroupement est une optimisation de la même responsabilité par décision, jamais un affaiblissement : chaque cellule reste décidée et journalisée individuellement.
Les consentements manuels (attest)
La portée attest couvre les consentements recueillis hors ligne et attestés par
l'entreprise. Quatre points d'accès :
POST /v1/manual-consentsenregistre un consentement attesté par l'entreprise. Le corps portecustomerId,documentVersionId,effectiveDate,validUntil, desitems(identifiants de catalogue ou paires{category, purpose}) et uneevidencedont lepdfSha256(empreinte SHA-256,0x+ 64 hex) est requis; lepdfen base64 est facultatif, pour la mise sous séquestre. La réponse201porteconsentId,merkleRoot, unconsentRefs(un0x-hex par cellule),assurance: "company_attested"etanchored(faux au moment de l'enregistrement).POST /v1/manual-consents/claim-linksémet un lien de réclamation à usage unique pour le citoyen : corps{ customerId }(requis) etreferencefacultatif; réponse201{ claimUrl, token, expiresAt }.POST /v1/manual-consents/{consentRef}/revokeporte le retrait du consentement : corps{ reason? }facultatif; réponse200{ consentRef, revoked: true, alreadyRevoked }. Une référence inconnue donne404.POST /v1/manual-consents/{consentRef}/eraseprocède à l'effacement (destruction cryptographique) : corps{ reason? }facultatif; réponse200{ consentRef, erased: true, alreadyErased }. Une référence inconnue donne404.
Un consentement manuel et un consentement signé par le citoyen autorisent de façon
identique; seul le palier d'assurance diffère (company_attested contre
citizen_signed). Les consentements manuels n'appliquent pas l'idempotence.
La fin d'une relation client (relationship)
POST /v1/customers/{customerRef}/relationship/end atteste, depuis votre propre
code (p. ex. un flux de désinscription), qu'une relation client est terminée, au
sens de l'article 23 de la Loi 25 (« les fins sont accomplies »). Portée :
relationship. C'est l'exposition /v1 de l'action de l'interface entreprise :
elle effectue la même écriture de cycle de vie terminale, si bien que la
justification requise, l'origine endedBy, la transition active/ending vers
ended et les obligations en aval se comportent de façon identique. C'est une
surcouche de cycle de vie : elle ne révoque, n'efface ni ne masque aucun
consentement par cellule.
Le client est identifié par votre propre customerRef (le même que pour
/v1/check), résolu au sein de l'entreprise de la clé. Une référence inconnue
ou étrangère donne un 404 propre, sans rien écrire. Un reason absent ou
vide donne 422 (jamais une fin silencieuse). L'appel est idempotent : re-finir
une relation déjà terminée est un succès; le endedAt et l'origine endedBy
d'origine sont préservés. Le point d'accès n'accepte ni ne renvoie jamais un DID de
citoyen.
{ "customerRef": "cust_8812", "status": "ended", "endedAt": "2026-06-26T15:04:05Z", "endedBy": "company" }
L'enveloppe d'erreur
Chaque erreur (jamais un refus) partage une seule forme :
{ "error": { "code": "invalid_request", "message": "…", "field": "category" } }
| HTTP | code |
Quand |
|---|---|---|
401 |
unauthorized |
Clé absente, invalide ou révoquée. |
403 |
forbidden |
Clé valide, portée manquante. |
422 |
invalid_request |
Entrée malformée (field nomme le fautif). Le seul 400 est le filtre status inconnu sur la liste des demandes. |
404 |
not_found |
Ressource introuvable. |
429 |
rate_limited |
Fenêtre par entreprise dépassée (voir Retry-After). |
Le code unavailable est réservé dans l'énumération, mais aucun point d'accès ne
l'émet pour l'instant.
Limites de débit
Les limites sont par entreprise, en fenêtre fixe (120 requêtes par minute par
défaut). Chaque point d'accès /v1 est limité. Un 429 porte le code
rate_limited et un en-tête Retry-After (secondes, entier, jusqu'à la
réinitialisation de la fenêtre). Il n'y a aucun autre en-tête de limite de débit.
Les SDK l'exposent comme une erreur typée avec retryAfter.
Idempotence
Seul POST /v1/consent-requests honore un en-tête Idempotency-Key (255
caractères au plus). Une nouvelle tentative avec la même clé pour la même
entreprise rejoue le 201 d'origine octet pour octet et ne crée rien de neuf,
de sorte qu'il n'y a ni double émission ni double courriel. Les SDK attachent
automatiquement une clé unique par appel; passez la vôtre pour rendre une nouvelle
tentative sûre. Les points d'accès de consentement manuel n'appliquent pas
l'idempotence.
Idempotency-Key: order-4471
Ensuite
- La référence
/v1interactive pour les schémas complets. - Les SDK TypeScript, SDK PHP et CLI encapsulent tout cela.