responsiblo

Pour les développeurs

API partenaire

Envoyez-nous un client sous votre propre identifiant pseudonyme - sans e-mail, sans nom ni autres données personnelles - et consultez à tout moment l'état de sa formation. Cela sert de contrôle de conformité avant un contrat, un dépôt ou un versement.

Version 1.3.2 · JSON via HTTPS · OpenAPI

Comment cela fonctionne

L'API est la même pour tous les partenaires. En cinq étapes :

  1. Nous activons l'API pour votre entreprise ; dans votre administration, vous créez une clé API dans l'onglet API.
  2. Lorsqu'un client doit être formé, votre système appelle POST /v1/users avec votre identifiant du client.
  3. Nous créons un profil sans e-mail et renvoyons un lien d'accès (entryUrl) ; vous le montrez au client sous forme de bouton.
  4. Le client clique dessus et se retrouve directement dans le cours.
  5. Vous consultez l'état à tout moment via GET /v1/users/{identifier}, ou nous l'annonçons à votre serveur par un webhook.

Vous êtes le responsable du traitement de l'identifiant, nous ne traitons que des données pseudonymisées. N'envoyez jamais un e-mail ni un nom comme identifiant - l'API refuse une adresse e-mail avec l'erreur 422.

Adresse et authentification

Chaque appel passe par HTTPS vers l'adresse ci-dessous et porte votre clé API dans l'en-tête Authorization.

https://api.responsiblo.com
Authorization: Bearer rsb_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  • rsb_live_... ouvre votre programme de production, rsb_test_... votre programme de test (sandbox).
  • La clé ne s'affiche qu'une fois, à sa création ; nous n'en conservons que l'empreinte. Gardez-la comme un secret sur votre serveur - jamais dans un navigateur ni dans une application.
  • La clé a des droits : read (l'état et la liste des personnes, le certificat) et write (l'envoi et l'anonymisation des personnes). Elle peut être limitée à vos adresses IP et avoir une validité.
  • Renouvellement : vous créez une nouvelle clé, l'ancienne reste valable 7 jours, puis elle expire.
  • Les réponses ne sont jamais mises en cache (Cache-Control: no-store) et l'API n'accepte pas d'appels depuis un navigateur (pas de CORS).

POST /v1/users

Envoie une personne dans le programme de la clé (droit write).

curl -X POST https://api.responsiblo.com/v1/users \
  -H "Authorization: Bearer $RSB_KEY" \
  -H "Content-Type: application/json" \
  -d '{"identifier":"client-48213","language":"cs","label":"campaign-2026-10"}'
ChampObligatoireSignification
identifier oui Votre pseudonyme de la personne, 1 à 64 caractères A-Z a-z 0-9 . _ : @ - ; jamais une adresse e-mail (refusée avec 422).
program non Code du programme ; si vous l'indiquez, ce doit être le programme de la clé.
language non Langue du cours (cs, en, ...) ; par défaut la première langue du programme.
label non Votre étiquette - une cohorte ou une campagne, 1 à 64 caractères, jamais une donnée personnelle.
expiresAt non Dernier jour d'accès, AAAA-MM-JJ.

201 Created pour une nouvelle personne, 200 OK si l'identifiant existe déjà - rien ne change, l'appel peut être répété sans risque. Le corps est l'état ci-dessous, avec entryUrl. Une limite de personnes ou de licences du programme épuisée renvoie 403.

GET /v1/users/{identifier}

État d'une personne (droit read) : ce dont un contrôle de conformité a besoin, rien de plus.

curl https://api.responsiblo.com/v1/users/client-48213 -H "Authorization: Bearer $RSB_KEY"

{
  "identifier": "client-48213",
  "program": "your-program",
  "status": "in_progress",
  "progress": 43,
  "lessonsCompleted": 3,
  "lessonsTotal": 7,
  "quizScore": null,
  "label": "campaign-2026-10",
  "createdAt": "2026-10-20T10:02:11Z",
  "startedAt": "2026-10-20T10:05:40Z",
  "completedAt": null,
  "lastActivityAt": "2026-10-20T10:31:40Z",
  "expiresAt": null,
  "certificate": null,
  "entryUrl": "https://your-company.responsiblo.com/entry/..."
}
statusSignification
enrolled envoyée, pas encore entrée
started entrée, rien terminé pour l'instant
in_progress a terminé des leçons, pas encore le programme
completed le programme est terminé (completedAt)
expired l'accès a pris fin (expiresAt est passé ; une personne de la sandbox s'arrête après 7 jours)

quizScore est le résultat du quiz final en %, dès qu'il est terminé. Les heures sont en UTC à la seconde entière. Le champ certificate porte le certificat de la personne (voir ci-dessous) ; tant qu'elle n'en a pas, il vaut null.

?include=detail

Ajoute la progression dans chaque cours et le résultat de chaque quiz - jamais les réponses individuelles.

"courses": [{"id": "733db340-...", "name": "Introduction", "progress": 40, "lessonsCompleted": 2, "lessonsTotal": 5, "completed": false}],
"quizzes": [{"id": "d2e72fd7-...", "name": "Quiz of the introduction", "score": 85, "completedAt": "2026-10-20T10:31:40Z"}]

GET /v1/users

Liste des personnes que vous avez envoyées dans le programme de la clé (droit read), page par page : chaque élément a la forme de l'état ci-dessus. En option status (uniquement les personnes dans cet état), limit (1 à 500, 100 par défaut) et cursor.

curl "https://api.responsiblo.com/v1/users?status=completed&limit=100" -H "Authorization: Bearer $RSB_KEY"

{
  "items": [{ "identifier": "client-48213", "status": "completed", "...": "the state of GET /v1/users/{identifier}" }],
  "nextCursor": "MjAyNi0xMC0yMFQxMDowMjoxMVp8...",
  "limit": 100
}

curl "https://api.responsiblo.com/v1/users?status=completed&limit=100&cursor=MjAyNi0xMC0yMFQxMDowMjoxMVp8..." -H "Authorization: Bearer $RSB_KEY"
  • Continuez avec cursor = nextCursor tant que nextCursor n'est pas null ; le curseur est un texte opaque, ne le composez jamais vous-même.
  • Les personnes viennent selon le moment de leur envoi, dans un ordre stable (y compris au même moment), aucune deux fois ; les personnes anonymisées ne figurent pas dans la liste.
  • Avec le filtre status, nous parcourons au plus 2000 personnes pour une page ; une page peut donc contenir moins d'éléments que limit, voire aucun - continuez tant qu'il y a un nextCursor.

Certificat de la personne

Dès que la personne termine le programme, le cours lui délivre un certificat - sous son pseudonyme (le surnom du profil ID XXXXXX), sans nom et sans e-mail. Rien n'est envoyé par e-mail ; l'état de la personne contient le champ certificate avec le numéro, les dates et la page publique de vérification, et vous téléchargez le PDF via GET /v1/users/{identifier}/certificate.

"certificate": {
  "number": "RSP-7KQ2-M4XH-9TWB",
  "issuedAt": "2026-10-20T10:39:15Z",
  "expiresAt": "2028-10-20T10:39:12Z",
  "verifyUrl": "https://responsiblo.com/verify/RSP-7KQ2-M4XH-9TWB"
}

curl https://api.responsiblo.com/v1/users/client-48213/certificate -H "Authorization: Bearer $RSB_KEY" -o client-48213.pdf
  • La page de vérification verifyUrl n'affiche que le pseudonyme, le programme, le partenaire et la validité - jamais votre identifiant.
  • Le PDF renvoie application/pdf (droit read). Sans certificat, la réponse est 404 certificateNotFound ; tant que le PDF est en cours de fabrication, 404 certificatePdfNotReady avec Retry-After.
  • Un certificat révoqué n'est jamais délivré (le champ certificate vaut alors null, le PDF 404) ; un certificat dont la validité a expiré reste une preuve véridique et il est délivré.
  • Un webhook annonce le certificat par l'événement certificate.issued (juste après user.completed).

DELETE /v1/users/{identifier}

Anonymise une personne (droit write) : votre identifiant, l'étiquette et chaque message de webhook qui le nomme disparaissent du profil ; les liens d'accès cessent d'être valables. Le certificat et le résultat du cours restent sous le numéro du certificat - comme preuve de conformité.

curl -X DELETE https://api.responsiblo.com/v1/users/client-48213 -H "Authorization: Bearer $RSB_KEY"

HTTP/1.1 204 No Content

Réponse 204 sans corps ; tout appel suivant avec le même identifiant renvoie 404 userNotFound, la répétition est donc sans risque. Vous pouvez ensuite renvoyer le même identifiant comme une nouvelle personne.

Lien d'accès

Montrez entryUrl à la personne (un bouton Démarrer le cours dans votre système). Il la connecte au cours - sans e-mail et sans mot de passe.

Le lien est valable 30 jours et peut être utilisé plusieurs fois ; chaque appel GET /v1/users/{identifier} en renvoie un nouveau. Ne l'envoyez jamais à quelqu'un d'autre : qui a le lien entre en tant que cette personne.

Webhooks

Dans l'onglet API de votre administration, vous configurez un webhook par programme : une adresse HTTPS publique de votre serveur et les événements. Nous vous envoyons ensuite un POST avec du JSON - uniquement des identifiants et des états, jamais le lien d'accès.

ÉvénementQuand
user.enrolled la personne a été envoyée (la première fois que votre webhook la connaît)
user.started la personne est entrée dans le cours
user.completed la personne a terminé le programme
certificate.issued la personne a obtenu un certificat ; data contient certificate (le numéro, les dates, verifyUrl)
user.expired l'accès de la personne a pris fin
POST https://your-server.example/responsiblo/webhook
Content-Type: application/json
X-Responsiblo-Event: user.completed
X-Responsiblo-Delivery: 5b0c1f9e-2d1a-4a57-9a3e-0d6c9c1f2b77
X-Responsiblo-Signature: t=1792492800,v1=3f1c...e9

{
  "id": "5b0c1f9e-2d1a-4a57-9a3e-0d6c9c1f2b77",
  "type": "user.completed",
  "createdAt": "2026-10-20T10:40:00Z",
  "apiVersion": "1.3.2",
  "program": "your-program",
  "data": {
    "identifier": "client-48213",
    "status": "completed",
    "progress": 100,
    "lessonsCompleted": 7,
    "lessonsTotal": 7,
    "quizScore": 85,
    "label": "campaign-2026-10",
    "startedAt": "2026-10-20T10:05:40Z",
    "completedAt": "2026-10-20T10:39:12Z",
    "expiresAt": null
  }
}

Signature

Chaque message porte l'en-tête X-Responsiblo-Signature: t=<temps Unix>,v1=<HMAC-SHA256 du texte t.corps avec le secret du webhook, hex>. Le secret (whsec_...) s'affiche une fois à la création du webhook. Avant de faire confiance à un message, vérifiez la signature et l'âge de t ; utilisez X-Responsiblo-Delivery comme clé d'idempotence.

// PHP: $secret = "whsec_...", $header = X-Responsiblo-Signature, $body = the raw body
preg_match('/^t=(\d+),v1=([0-9a-f]{64})$/', $header, $m);
$valid = $m
    && abs(time() - (int) $m[1]) <= 300
    && hash_equals(hash_hmac('sha256', $m[1] . '.' . $body, $secret), $m[2]);

Livraison et nouvelles tentatives

Livré = réponse 2xx en moins de 10 secondes. Sinon, nous réessayons après 1 minute, 5 minutes, 30 minutes, 2 heures et 12 heures, puis nous abandonnons. Nous ne suivons pas les redirections.

20 messages non livrés d'affilée désactivent le webhook jusqu'à ce que vous le réactiviez ; les messages de cette période attendent. Le bouton Test envoie un ping ; le journal des livraisons se trouve dans votre administration.

Vous ajoutez l'événement certificate.issued à un webhook dans ses paramètres ; un webhook créé avant la version 1.2 ne le reçoit pas de lui-même.

Erreurs

Chaque erreur a une seule forme : status, code, reason et message ; une erreur 422 ajoute errors (champ - ce qui ne va pas).

{ "status": "error", "code": 422, "reason": "invalidBody", "message": "The body is not valid.",
  "errors": { "identifier": "identifier must be a pseudonym of the partner, never an e-mail address" } }
HTTPreasonQue faire
400 invalidJson envoyez un seul objet JSON
401 missingToken, invalidToken envoyez une clé valide (une clé révoquée et une clé expirée se ressemblent) ; l'en-tête WWW-Authenticate indique Bearer
403 scopeDenied, ipNotAllowed, apiDisabled, partnerInactive, spaceUnavailable, httpsRequired, userLimitReached, seatsExhausted la clé n'a pas ce droit, l'API est désactivée pour votre entreprise, ou le programme est complet
404 userNotFound, notFound aucune personne avec cet identifiant dans le programme de la clé (pas même anonymisée)
404 certificateNotFound, certificatePdfNotReady la personne n'a pas encore de certificat, ou son PDF est en cours de fabrication - réessayez après Retry-After
409 identifierTaken l'identifiant appartient à une personne d'un autre programme
422 invalidBody, invalidParameter voir errors (un champ du corps ou un paramètre de la requête - ce qui ne va pas)
429 rateLimited attendez Retry-After secondes
500 internalError réessayez plus tard ; indiquez incidentId à notre support

Pour les programmes et les agents

  • Décidez selon code et reason, jamais selon le texte de message ; errors est un objet champ -> ce qui ne va pas.
  • Ne répétez que 429, 500 et certificatePdfNotReady, après Retry-After secondes (sinon avec un intervalle croissant) ; ne répétez pas les autres 4xx que 429 sans corriger l'appel.
  • POST /v1/users est idempotent selon identifier (201 la première fois, puis 200) ; DELETE renvoie 204 la première fois et 404 ensuite - les deux peuvent être répétés sans risque.
  • Dédupliquez les webhooks selon X-Responsiblo-Delivery ; l'ordre des messages n'est pas garanti, c'est l'état dans data qui décide.

Guide du prêt

Le guide du prêt est un court cours destiné à la personne qui demande un prêt auprès d'un prêteur : avant de signer le contrat, elle suit trois leçons, huit situations pratiques et, le cas échéant, un quiz, le tout avec les chiffres de sa propre offre, puis elle confirme l'offre à la fin. Sur chaque écran, elle peut demander une explication. Vous envoyez l'offre via l'API, vous recevez un lien d'accès et un webhook vous annonce le résultat.

Nous activons le guide du prêt en accord avec vous.

Ici aussi, la personne qui fait la demande n'est que votre profil pseudonymisé issu de POST /v1/users. L'offre est déterminée par vos références (externalApplicationId, offerReference) et les documents uniquement par un lien avec une empreinte SHA-256 - jamais un nom, un e-mail, un numéro d'identification national, un numéro de compte ni un téléphone. responsiblo n'approuve pas le prêt, ne télécharge ni ne conserve les documents, ne vérifie pas l'identité et ne délivre aucun certificat pour le guide.

  1. Vous créez le profil de la personne qui fait la demande : POST /v1/users avec votre pseudonyme.
  2. Vous envoyez l'offre : POST /v1/users/{identifier}/loan-offers ; la réponse 201 porte entryUrl.
  3. Vous transmettez entryUrl à la personne par votre propre canal vérifié. Le lien est valable 30 jours et peut être utilisé plusieurs fois ; le guide affiche les chiffres de son offre.
  4. Vous apprenez la fin par un webhook (loanGuide.completed, loanGuide.assistanceRequested et d'autres).
  5. Vous lisez l'état via GET …/loan-offers/{offerReference}, l'enregistrement pour votre dossier via GET …/evidence. Offre modifiée = nouvelle offre avec supersedes, demande retirée = POST …/cancel.

Jusqu'à l'activation, les opérations du guide répondent 404 notFound ou 409 loanGuideNotConfigured. Le droit write envoie et annule une offre, read lit l'état et la preuve.

POST /v1/users/{identifier}/loan-offers

Envoie une offre pour le profil de la personne qui fait la demande (droit write). L'en-tête facultatif Idempotency-Key compte de 1 à 255 caractères ASCII visibles. Exemple : prêt de 10 000 CZK, 10 800 CZK à payer en un seul paiement 30 jours après le versement, TAEG 155,10 %.

curl -X POST https://api.responsiblo.com/v1/users/applicant-30417/loan-offers \
  -H "Authorization: Bearer $RSB_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: offer-30417-1" \
  -d '{
  "externalApplicationId": "APP-30417",
  "offerReference": "OFF-30417-1",
  "locale": "cs-CZ",
  "currency": "CZK",
  "principalMinor": 1000000,
  "disbursedAmountMinor": 1000000,
  "totalPayableMinor": 1080000,
  "aprPercent": "155.10",
  "interestRate": { "percent": "8", "period": "month" },
  "repayment": {
    "type": "singlePayment",
    "amountMinor": 1080000,
    "due": { "kind": "relative", "days": 30, "anchor": "disbursement" }
  },
  "documents": [
    { "role": "precontract_information", "reference": "DOC-PRE-30417", "title": "Předsmluvní informace",
      "url": "https://documents.lender.example/pre/30417.pdf",
      "sha256": "623f4a37e444268c076896d6c2f389b46f568d1a26539b013f2ba25d03846f72" },
    { "role": "contract_package", "reference": "DOC-CON-30417", "title": "Smlouva a podmínky",
      "url": "https://documents.lender.example/contract/30417.pdf",
      "sha256": "9c7bfe0077d1189914f0e1c483c961f4c5b36e9892385f3d2ab6168e0c088aa6" }
  ],
  "validUntil": "2026-11-15T18:00:00Z"
}'

HTTP/1.1 201 Created

{
  "offerReference": "OFF-30417-1",
  "externalApplicationId": "APP-30417",
  "offerStatus": "active",
  "guideStatus": "created",
  "validUntil": "2026-11-15T18:00:00Z",
  "snapshotSha256": "8055bfa3c5f2c163691b85785eaf61221169a123bed67aff2c1dbaf2a8e44e7e",
  "entryUrl": "https://your-company.responsiblo.com/entry/...",
  "createdAt": "2026-11-10T09:14:05Z"
}
ChampObligatoireSignification
externalApplicationId oui Votre numéro de demande, 1 à 128 caractères A-Z a-z 0-9 . _ : -; jamais une adresse e-mail.
offerReference oui Votre référence de l'offre, mêmes règles que externalApplicationId.
locale oui Langue et pays du guide : cs-CZ.
currency oui Devise : CZK.
principalMinor oui Montant du prêt en hellers (centièmes de couronne), nombre entier de 1 à 10 000 000 000.
disbursedAmountMinor oui Montant versé en hellers ; il doit être égal au montant du prêt.
totalPayableMinor oui Montant total à payer en hellers ; au moins le montant du prêt et exactement la somme des paiements.
aprPercent oui TAEG en pourcentage sous forme de texte ("155.10"), calculé et garanti par vous. L'API refuse des coûts positifs avec un TAEG de 0.
interestRate non Taux d'intérêt {percent, period}, period = year, month ou day.
repayment oui Remboursement : singlePayment {amountMinor, due} avec une date (kind date) ou un nombre de jours à compter du versement (kind relative, 1 à 365), ou installments {schedule: [{date, amountMinor}]} avec 1 à 360 échéances par ordre croissant, aucune dans le passé. Le type doit correspondre au produit configuré pour votre guide.
documents oui 2 à 10 documents {role, reference, title, url, sha256} ; les rôles exigés par les paramètres de votre guide doivent être couverts (par défaut precontract_information et contract_package). url uniquement en HTTPS sur un hôte autorisé dans les paramètres de votre guide, 2 048 caractères au plus, sans identifiants de connexion ; sha256 = 64 caractères hexadécimaux que vous déclarez.
validUntil oui Validité de l'offre, RFC 3339 avec décalage horaire, dans le futur et au plus 7 jours à l'avance.
supersedes non offerReference de l'offre active du même profil que la nouvelle offre remplace.
returnUrl non Facultatif : l'adresse vers laquelle la page « Terminé » renvoie le demandeur à la fin du guide. HTTPS absolu uniquement, 2 048 caractères au maximum, sans identifiants ; nous remplaçons les symboles {offerReference} et {externalApplicationId} par les références de l'offre. Sans elle, l'adresse de retour des paramètres de votre guide s'applique.

Le même corps envoyé à nouveau (sous les mêmes références ou la même Idempotency-Key) renvoie 200 avec l'offre existante et un nouvel entryUrl ; un autre corps sous celles-ci renvoie 409 offerConflict. Un profil a une seule offre active : une autre sans supersedes renvoie 409 activeOfferExists, un supersedes erroné 409 notSupersedable. Les paramètres de votre guide (quiz, contact) sont alors verrouillés pour l'offre.

État, preuve et annulation

  • GET /v1/users/{identifier}/loan-offers/{offerReference} - état (droit read) : offerStatus (active, expired, superseded, cancelled), guideStatus (created, in_progress, completed), leçons terminées, pratique, quiz (null s'il est désactivé) et les moments de la présentation de l'offre, de la confirmation et de la demande d'explication. Jamais les réponses, les valeurs de l'offre ni le lien. L'achèvement reste acquis même si l'offre expire plus tard.
  • GET /v1/users/{identifier}/loan-offers/{offerReference}/evidence - enregistrement pour votre dossier (droit read) : schemaVersion loan-guide-evidence/1, instantané de votre offre avec son empreinte snapshotSha256, édition du cours avec les empreintes des leçons, événements dans l'ordre de sequence, chaque réponse avec toutes ses tentatives, la confirmation et limitations - ce que l'enregistrement ne prouve pas (présenté ne signifie pas lu, l'identité est affirmée par vous, les documents uniquement par référence, aucun certificat, aucune approbation du prêt).
  • POST /v1/users/{identifier}/loan-offers/{offerReference}/cancel - annule une offre active (droit write), reasonCode = withdrawn_by_provider ou application_closed ; 200 renvoie l'état, une offre qui n'est pas active ou dont le guide est terminé renvoie 409 notActive.
curl https://api.responsiblo.com/v1/users/applicant-30417/loan-offers/OFF-30417-1 -H "Authorization: Bearer $RSB_KEY"

{
  "offerReference": "OFF-30417-1",
  "externalApplicationId": "APP-30417",
  "offerStatus": "active",
  "guideStatus": "completed",
  "quizEnabled": true,
  "lessonsCompleted": 3,
  "lessonsTotal": 3,
  "practiceCompleted": true,
  "quizPassed": true,
  "offerPresentedAt": "2026-11-10T18:32:47Z",
  "confirmedAt": "2026-11-10T18:34:02Z",
  "completedAt": "2026-11-10T18:34:02Z",
  "assistanceRequested": false,
  "assistanceRequestedAt": null,
  "validUntil": "2026-11-15T18:00:00Z",
  "evidenceErased": false,
  "...": "..."
}

curl -X POST https://api.responsiblo.com/v1/users/applicant-30417/loan-offers/OFF-30417-1/cancel \
  -H "Authorization: Bearer $RSB_KEY" \
  -H "Content-Type: application/json" \
  -d '{"reasonCode":"withdrawn_by_provider"}'

?externalApplicationId= n'est nécessaire que lorsqu'une offerReference appartient à plusieurs demandes du profil (sinon 409 ambiguousOfferReference).

Webhooks du guide

Le webhook du programme (voir Webhooks) reçoit les événements du guide sans abonnement particulier. La signature, la livraison et les nouvelles tentatives sont les mêmes que pour les autres événements ; data portent identifier, offerReference, externalApplicationId, guideStatus, occurredAt et sequence (l'ordre des événements d'une offre) - jamais la preuve, les valeurs de l'offre ni le lien d'accès.

ÉvénementQuand
loanGuide.completed la personne a parcouru le guide et confirmé l'offre
loanGuide.assistanceRequested la personne a demandé une explication (appelez-la)
loanGuide.expired le validUntil de l'offre est dépassé
loanGuide.superseded une nouvelle offre l'a remplacée (supersedes)
loanGuide.cancelled vous avez annulé l'offre
POST https://your-server.example/responsiblo/webhook
Content-Type: application/json
X-Responsiblo-Event: loanGuide.completed
X-Responsiblo-Delivery: 0f6e2a4c-91d3-4b8e-a7c5-3e2d1b9f6a08
X-Responsiblo-Signature: t=1794335647,v1=8b2d...4c

{
  "id": "0f6e2a4c-91d3-4b8e-a7c5-3e2d1b9f6a08",
  "type": "loanGuide.completed",
  "createdAt": "2026-11-10T18:34:07Z",
  "apiVersion": "1.3.2",
  "program": "your-program",
  "data": {
    "identifier": "applicant-30417",
    "offerReference": "OFF-30417-1",
    "externalApplicationId": "APP-30417",
    "guideStatus": "completed",
    "occurredAt": "2026-11-10T18:34:02Z",
    "sequence": 23
  }
}

Chaque événement parvient au plus une fois à un même webhook (clé d'idempotence = X-Responsiblo-Delivery). Un webhook créé plus tard ne rejoue pas les événements plus anciens, et les événements de plus de 28 jours ne sont plus annoncés.

Conservation de la preuve

Lorsque le parcours d'une offre se termine (offre terminée, annulée, expirée ou remplacée), la durée de conservation des paramètres de votre guide court (30 jours par défaut). Ensuite, la preuve est supprimée : GET …/evidence renvoie 410 evidenceErased et l'état continue de répondre avec evidenceErased: true, les références et les moments. Une offre active n'est jamais supprimée. Téléchargez la preuve que vous devez conserver juste après loanGuide.completed.

Erreurs du guide

Les erreurs ont la forme de l'API ci-dessus. En plus, pour le guide :

HTTPreasonQue faire
404 offerNotFound le profil n'a pas une telle offre (userNotFound = le profil n'existe pas)
409 offerConflict, activeOfferExists, notSupersedable les références ou l'Idempotency-Key appartiennent déjà à un autre corps, ou le profil a une autre offre active - envoyez supersedes
409 loanGuideNotConfigured le guide n'est pas activé pour votre programme - écrivez-nous
409 ambiguousOfferReference ajoutez ?externalApplicationId=
409 notActive l'offre n'est pas active ou son guide est terminé
410 evidenceErased la preuve a été supprimée après la durée de conservation, l'état reste
422 invalidOffer errors = {chemin: code[,code]} avec le chemin dans le corps (repayment.schedule[2].date, documents[0].url) ; les codes dans le tableau ci-dessous
422 invalidBody supersedes, Idempotency-Key ou corps de l'annulation
errorsCodes de l'erreur invalidOffer
required le champ manque
invalidFormat forme incorrecte (type, motif, longueur, lien sans HTTPS ou avec identifiants de connexion)
outOfRange hors de la plage autorisée (montant, nombre de documents, d'échéances ou de jours)
notEqualPrincipal le montant versé n'est pas égal au montant du prêt
sumMismatch la somme des paiements n'est pas égale au montant total à payer
belowPrincipal le montant total à payer est inférieur au montant du prêt
aprContradiction TAEG de 0 avec des coûts positifs
typeMismatch le type de remboursement ne correspond pas au produit configuré pour votre guide
missingRole il manque un document avec un rôle exigé par les paramètres de votre guide
hostNotAllowed le lien d'un document pointe vers un hôte que les paramètres de votre guide n'autorisent pas
notAscending les dates des échéances ne sont pas croissantes ou se répètent
inPast la date d'une échéance ou validUntil est dans le passé
tooFar validUntil est à plus de 7 jours dans le futur
unsupported valeur non prise en charge (locale, currency, type, period, kind)
unknownField champ que l'API ne connaît pas
looksLikeEmail une référence ressemble à une adresse e-mail

Une erreur ne répète jamais une valeur que vous avez envoyée. Exemple : la même offre avec un paiement de 10 700 CZK au lieu de 10 800 CZK et une validité de 9 jours :

"repayment": { "type": "singlePayment", "amountMinor": 1070000, "due": { "kind": "relative", "days": 30, "anchor": "disbursement" } },
"validUntil": "2026-11-19T18:00:00Z"

HTTP/1.1 422 Unprocessable Entity

{
  "status": "error",
  "code": 422,
  "reason": "invalidOffer",
  "message": "The offer contains invalid data.",
  "errors": {
    "repayment.amountMinor": "sumMismatch",
    "validUntil": "tooFar"
  }
}

Limites

120 appels par minute et par clé. Après 30 clés refusées depuis une même adresse en dix minutes, l'adresse attend 10 minutes.

Nous conservons le journal des appels (sans corps, l'identifiant uniquement sous forme d'empreinte SHA-256) 180 jours, les messages de webhook livrés et abandonnés 30 jours.

État et contrat

  • GET /v1/status - sans clé, renvoie {"status":"ok","version":"1.3.2"}
  • GET /openapi - sans clé, le document OpenAPI 3 de l'API partenaire

Sandbox

La clé de test rsb_test_... ouvre votre programme de test avec le même cours. Les personnes y prennent fin après 7 jours ; le webhook du programme de test est séparé de celui de production.

Historique des versions

  • 1.3.2 (2026-11) - Chaque opération du document OpenAPI a un nom et une description, et les opérations sont réparties en groupes. /v1/status et les webhooks indiquent la version 1.3.2. La forme des requêtes et des réponses reste inchangée.
  • 1.3.1 (2026-10) - Corrections sans changement des endpoints ni de leurs réponses.
  • 1.3.0 (2026-10) - Guide du prêt : POST /v1/users/{identifier}/loan-offers, état, preuve et annulation d'une offre, webhooks loanGuide.*, conservation de la preuve. Champ facultatif returnUrl d'une offre (adresse de retour propre).
  • 1.2.0 (2026-10) - GET /v1/users avec pagination, le certificat de la personne (champ certificate, GET /v1/users/{identifier}/certificate, événement certificate.issued), DELETE /v1/users/{identifier} (anonymisation), identifiant de 64 caractères au plus, conservation du journal.
  • 1.1.0 (2026-10) - Clés API, POST /v1/users, GET /v1/users/{identifier} avec ?include=detail, lien d'accès, webhooks, une seule forme d'erreurs, état public et OpenAPI, limites.
  • 1.0.0 (2024) - GET /v1/user/{identifier} avec le JWT de la clé partenaire.

Vous voulez l'API pour votre entreprise ? Écrivez-nous