Per gli sviluppatori
API per partner
Inviateci un cliente con il vostro identificativo pseudonimo - senza e-mail, nome e altri dati personali - e leggete in qualsiasi momento lo stato della sua formazione. Serve come controllo di compliance prima di un contratto, di un deposito o di un pagamento.
Versione 1.3.2 · JSON su HTTPS · OpenAPI
Come funziona
L'API è uguale per tutti i partner. In cinque passaggi:
- Attiviamo l'API per la vostra azienda; nella vostra amministrazione create una chiave API nella scheda API.
- Quando un cliente deve essere formato, il vostro sistema chiama POST /v1/users con il vostro identificativo del cliente.
- Creiamo un profilo senza e-mail e restituiamo un link di accesso (entryUrl); lo mostrate al cliente come pulsante.
- Il cliente ci fa clic ed è subito nel corso.
- Leggete lo stato in qualsiasi momento tramite GET /v1/users/{identifier}, oppure lo comunichiamo al vostro server con un webhook.
Il titolare del trattamento dell'identificativo siete voi, noi trattiamo solo dati pseudonimizzati. Non inviate mai come identificativo un'e-mail o un nome - l'API rifiuta un indirizzo e-mail con l'errore 422.
Indirizzo e autenticazione
Ogni chiamata passa via HTTPS all'indirizzo qui sotto e porta la vostra chiave API nell'intestazione Authorization.
https://api.responsiblo.com Authorization: Bearer rsb_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
- rsb_live_... apre il vostro programma di produzione, rsb_test_... il vostro programma di test (sandbox).
- La chiave viene mostrata una sola volta, alla creazione; ne conserviamo solo l'impronta. Custoditela come segreto sul vostro server - mai in un browser né in un'app.
- La chiave ha delle autorizzazioni: read (lo stato e l'elenco delle persone, il certificato) e write (l'invio e l'anonimizzazione delle persone). Può essere limitata ai vostri indirizzi IP e avere una validità.
- Sostituzione: create una nuova chiave, la vecchia resta valida ancora 7 giorni, poi scade.
- Le risposte non vengono mai memorizzate nella cache (Cache-Control: no-store) e l'API non accetta chiamate da un browser (niente CORS).
POST /v1/users
Invia una persona al programma della chiave (autorizzazione 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"}'
| Campo | Obbligatorio | Significato |
|---|---|---|
identifier |
sì | Il vostro pseudonimo della persona, da 1 a 64 caratteri A-Z a-z 0-9 . _ : @ -; mai un indirizzo e-mail (rifiutato con 422). |
program |
no | Codice del programma; se lo indicate, deve essere il programma della chiave. |
language |
no | Lingua del corso (cs, en, ...); per impostazione predefinita la prima lingua del programma. |
label |
no | La vostra etichetta - una coorte o una campagna, da 1 a 64 caratteri, mai un dato personale. |
expiresAt |
no | Ultimo giorno di accesso, AAAA-MM-GG. |
201 Created per una nuova persona, 200 OK se l'identificativo esiste già - non cambia nulla, la chiamata si può ripetere in sicurezza. Il corpo è lo stato qui sotto, con entryUrl. Un limite esaurito di persone o di licenze del programma restituisce 403.
GET /v1/users/{identifier}
Stato di una persona (autorizzazione read): ciò che serve a un controllo di compliance, niente di più.
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/..."
}
status | Significato |
|---|---|
enrolled |
inviata, non ancora entrata |
started |
entrata, finora non ha completato nulla |
in_progress |
ha completato delle lezioni, il programma non ancora |
completed |
il programma è completato (completedAt) |
expired |
l'accesso è terminato (expiresAt è trascorso; una persona nella sandbox termina dopo 7 giorni) |
quizScore è il risultato del quiz finale in %, appena completato. Gli orari sono in UTC al secondo intero. Il campo certificate contiene il certificato della persona (vedi sotto); finché non ce l'ha, è null.
?include=detail
Aggiunge l'avanzamento in ogni corso e il risultato di ogni quiz - mai le singole risposte.
"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
Elenco delle persone che avete inviato al programma della chiave (autorizzazione read), pagina per pagina: ogni voce ha la forma dello stato qui sopra. Facoltativi status (solo le persone in quello stato), limit (da 1 a 500, predefinito 100) e 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"
- Proseguite con cursor = nextCursor finché nextCursor non è null; il cursore è un testo opaco, non componetelo mai da soli.
- Le persone seguono il momento dell'invio in un ordine stabile (anche nello stesso momento), nessuna due volte; le persone anonimizzate non sono nell'elenco.
- Con il filtro status esaminiamo al massimo 2000 persone per pagina, quindi una pagina può avere meno voci di limit, anche nessuna - proseguite finché c'è un nextCursor.
Certificato della persona
Appena la persona completa il programma, il corso le rilascia un certificato - con il suo pseudonimo (il nickname del profilo ID XXXXXX), senza nome e senza e-mail. Nulla viene inviato per e-mail; lo stato della persona contiene il campo certificate con il numero, gli orari e la pagina pubblica di verifica, e il PDF lo scaricate tramite 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 pagina di verifica verifyUrl mostra solo lo pseudonimo, il programma, il partner e la validità - mai il vostro identificativo.
- Il PDF restituisce application/pdf (autorizzazione read). Senza certificato la risposta è 404 certificateNotFound; mentre il PDF è ancora in preparazione, 404 certificatePdfNotReady con Retry-After.
- Un certificato revocato non viene mai consegnato (il campo certificate è allora null, il PDF 404); un certificato con validità scaduta resta una prova veritiera e viene consegnato.
- Un webhook annuncia il certificato con l'evento certificate.issued (subito dopo user.completed).
DELETE /v1/users/{identifier}
Anonimizza una persona (autorizzazione write): dal profilo scompaiono il vostro identificativo, l'etichetta e ogni messaggio del webhook che lo nomina; i link di accesso smettono di essere validi. Il certificato e il risultato del corso restano sotto il numero del certificato - come prova di compliance.
curl -X DELETE https://api.responsiblo.com/v1/users/client-48213 -H "Authorization: Bearer $RSB_KEY" HTTP/1.1 204 No Content
Risposta 204 senza corpo; ogni chiamata successiva con lo stesso identificativo restituisce 404 userNotFound, quindi la ripetizione è sicura. Lo stesso identificativo potete poi inviarlo di nuovo come una nuova persona.
Link di accesso
Mostrate entryUrl alla persona (un pulsante Avvia il corso nel vostro sistema). La fa accedere al corso - senza e-mail e senza password.
Il link è valido 30 giorni e si può usare più volte; ogni chiamata GET /v1/users/{identifier} ne restituisce uno nuovo. Non inviatelo mai a nessun altro: chi ha il link entra come quella persona.
Webhook
Nella scheda API della vostra amministrazione impostate un webhook per ogni programma: un indirizzo HTTPS pubblico del vostro server e gli eventi. Da quel momento vi inviamo un POST con JSON - solo identificativi e stati, mai il link di accesso.
| Evento | Quando |
|---|---|
user.enrolled |
la persona è stata inviata (la prima volta che il vostro webhook la conosce) |
user.started |
la persona è entrata nel corso |
user.completed |
la persona ha completato il programma |
certificate.issued |
la persona ha ottenuto un certificato; data contiene certificate (il numero, gli orari, verifyUrl) |
user.expired |
l'accesso della persona è terminato |
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
}
}
Firma
Ogni messaggio porta l'intestazione X-Responsiblo-Signature: t=<tempo Unix>,v1=<HMAC-SHA256 del testo t.corpo con il segreto del webhook, hex>. Il segreto (whsec_...) viene mostrato una volta alla creazione del webhook. Prima di fidarvi di un messaggio, verificate la firma e l'età di t; come chiave di idempotenza usate X-Responsiblo-Delivery.
// 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]);
Consegna e nuovi tentativi
Consegnato = risposta 2xx entro 10 secondi. Altrimenti riproviamo dopo 1 minuto, 5 minuti, 30 minuti, 2 ore e 12 ore, poi rinunciamo. Non seguiamo i reindirizzamenti.
20 messaggi non consegnati di seguito disattivano il webhook finché non lo riattivate; i messaggi di quel periodo restano in attesa. Il pulsante Test invia un ping; il registro delle consegne si trova nella vostra amministrazione.
L'evento certificate.issued si aggiunge a un webhook nelle sue impostazioni; un webhook creato prima della versione 1.2 non lo riceve da solo.
Errori
Ogni errore ha un'unica forma: status, code, reason e message; un errore 422 aggiunge errors (campo - che cosa non va).
{ "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" } }
| HTTP | reason | Che cosa fare |
|---|---|---|
| 400 | invalidJson |
inviate un unico oggetto JSON |
| 401 | missingToken, invalidToken |
inviate una chiave valida (una chiave revocata e una scaduta hanno lo stesso aspetto); l'intestazione WWW-Authenticate indica Bearer |
| 403 | scopeDenied, ipNotAllowed, apiDisabled, partnerInactive, spaceUnavailable, httpsRequired, userLimitReached, seatsExhausted |
la chiave non ha questa autorizzazione, l'API è disattivata per la vostra azienda, oppure il programma è pieno |
| 404 | userNotFound, notFound |
nel programma della chiave non c'è nessuna persona con questo identificativo (nemmeno anonimizzata) |
| 404 | certificateNotFound, certificatePdfNotReady |
la persona non ha ancora il certificato, oppure il suo PDF è in preparazione - riprovate dopo Retry-After |
| 409 | identifierTaken |
l'identificativo appartiene a una persona di un altro programma |
| 422 | invalidBody, invalidParameter |
vedi errors (un campo del corpo o un parametro della query - che cosa non va) |
| 429 | rateLimited |
attendete Retry-After secondi |
| 500 | internalError |
riprovate più tardi; comunicate incidentId al nostro supporto |
Per programmi e agenti
- Decidete in base a code e reason, mai in base al testo di message; errors è un oggetto campo -> che cosa non va.
- Ripetete solo 429, 500 e certificatePdfNotReady, e solo dopo Retry-After secondi (altrimenti con un intervallo crescente); non ripetete altri 4xx oltre a 429 senza correggere la chiamata.
- POST /v1/users è idempotente per identifier (201 la prima volta, poi 200); DELETE restituisce 204 la prima volta e 404 in seguito - entrambe si possono ripetere in sicurezza.
- Deduplicate i webhook in base a X-Responsiblo-Delivery; l'ordine dei messaggi non è garantito, decide lo stato in data.
Guida al prestito
La guida al prestito è un breve corso per chi richiede un prestito a un finanziatore: prima di firmare il contratto, la persona richiedente segue tre lezioni, otto situazioni pratiche ed eventualmente un quiz, tutto sui numeri della propria offerta, e alla fine conferma l'offerta. In ogni schermata può chiedere una spiegazione. Inviate l'offerta tramite l'API, ricevete un link di accesso e un webhook vi comunica il risultato.
Attiviamo la guida al prestito in accordo con voi.
Anche qui la persona richiedente è solo il vostro profilo pseudonimo di POST /v1/users. L'offerta è identificata dai vostri riferimenti (externalApplicationId, offerReference) e i documenti solo da un link con impronta SHA-256 - mai nome, e-mail, codice fiscale, numero di conto o telefono. responsiblo non approva il prestito, non scarica né conserva i documenti, non verifica l'identità e non rilascia alcun certificato per la guida.
- Create il profilo della persona richiedente: POST /v1/users con il vostro pseudonimo.
- Inviate l'offerta: POST /v1/users/{identifier}/loan-offers; la risposta 201 contiene entryUrl.
- Consegnate entryUrl alla persona richiedente tramite un vostro canale verificato. Il link è valido 30 giorni e si può usare più volte; la guida mostra i numeri della sua offerta.
- La conclusione vi viene comunicata da un webhook (loanGuide.completed, loanGuide.assistanceRequested e altri).
- Leggete lo stato tramite GET …/loan-offers/{offerReference}, le evidenze per il vostro fascicolo tramite GET …/evidence. Offerta modificata = nuova offerta con supersedes, richiesta ritirata = POST …/cancel.
Fino all'attivazione le operazioni della guida rispondono 404 notFound o 409 loanGuideNotConfigured. L'autorizzazione write invia e annulla un'offerta, read legge lo stato e le evidenze.
POST /v1/users/{identifier}/loan-offers
Invia un'offerta al profilo della persona richiedente (autorizzazione write). L'header facoltativo Idempotency-Key ha da 1 a 255 caratteri ASCII visibili. Esempio: prestito di 10.000 CZK, 10.800 CZK da pagare in un unico pagamento 30 giorni dopo l'erogazione, 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"
}
| Campo | Obbligatorio | Significato |
|---|---|---|
externalApplicationId |
sì | Il vostro numero di richiesta, da 1 a 128 caratteri A-Z a-z 0-9 . _ : -; mai un indirizzo e-mail. |
offerReference |
sì | Il vostro riferimento dell'offerta, stesse regole di externalApplicationId. |
locale |
sì | Lingua e paese della guida: cs-CZ. |
currency |
sì | Valuta: CZK. |
principalMinor |
sì | Importo del prestito in centesimi di corona, numero intero da 1 a 10.000.000.000. |
disbursedAmountMinor |
sì | Importo erogato in centesimi di corona; deve essere uguale all'importo del prestito. |
totalPayableMinor |
sì | Importo totale dovuto in centesimi di corona; almeno pari all'importo del prestito ed esattamente la somma dei pagamenti. |
aprPercent |
sì | TAEG in percentuale come testo ("155.10"), calcolato e garantito da voi. L'API rifiuta costi positivi con TAEG 0. |
interestRate |
no | Tasso di interesse {percent, period}, period = year, month o day. |
repayment |
sì | Rimborso: singlePayment {amountMinor, due} con una data (kind date) o un numero di giorni dall'erogazione (kind relative, da 1 a 365), oppure installments {schedule: [{date, amountMinor}]} con da 1 a 360 rate in ordine crescente, nessuna nel passato. Il tipo deve corrispondere al prodotto impostato per la vostra guida. |
documents |
sì | Da 2 a 10 documenti {role, reference, title, url, sha256}; devono coprire i ruoli richiesti dalle impostazioni della vostra guida (per impostazione predefinita precontract_information e contract_package). url solo HTTPS su un host consentito nelle impostazioni della vostra guida, al massimo 2.048 caratteri, senza credenziali; sha256 = 64 caratteri esadecimali dichiarati da voi. |
validUntil |
sì | Validità dell'offerta, RFC 3339 con offset di fuso orario, nel futuro e al massimo 7 giorni avanti. |
supersedes |
no | offerReference dell'offerta attiva dello stesso profilo che la nuova offerta sostituisce. |
returnUrl |
no | Facoltativo: l'indirizzo a cui la pagina «Completato» riporta il richiedente alla fine della guida. Solo HTTPS assoluto, al massimo 2.048 caratteri, senza credenziali; sostituiamo i simboli {offerReference} e {externalApplicationId} con i riferimenti dell'offerta. Senza di esso vale l'indirizzo di ritorno delle impostazioni della vostra guida. |
Lo stesso corpo di nuovo (con gli stessi riferimenti o lo stesso Idempotency-Key) restituisce 200 con l'offerta esistente e un nuovo entryUrl; un corpo diverso con essi restituisce 409 offerConflict. Un profilo ha un'unica offerta attiva: un'altra senza supersedes restituisce 409 activeOfferExists, un supersedes errato 409 notSupersedable. Le impostazioni della vostra guida (quiz, contatto) vengono in quel momento bloccate sull'offerta.
Stato, evidenze e annullamento
GET /v1/users/{identifier}/loan-offers/{offerReference}- lo stato (autorizzazione read): offerStatus (active, expired, superseded, cancelled), guideStatus (created, in_progress, completed), lezioni completate, situazioni pratiche, quiz (null se è disattivato) e i momenti della presentazione dell'offerta, della conferma e della richiesta di spiegazione. Mai le risposte, i valori dell'offerta o il link. Il completamento resta anche se l'offerta scade in seguito.GET /v1/users/{identifier}/loan-offers/{offerReference}/evidence- le evidenze per il vostro fascicolo (autorizzazione read): schemaVersion loan-guide-evidence/1, un'istantanea della vostra offerta con l'impronta snapshotSha256, l'edizione del corso con le impronte delle lezioni, gli eventi nell'ordine di sequence, ogni risposta con tutti i tentativi, la conferma e limitations - ciò che le evidenze non dimostrano (presentato non significa letto, l'identità è dichiarata da voi, i documenti solo come riferimento, nessun certificato, nessuna approvazione del prestito).POST /v1/users/{identifier}/loan-offers/{offerReference}/cancel- annulla un'offerta attiva (autorizzazione write), reasonCode = withdrawn_by_provider o application_closed; 200 restituisce lo stato, un'offerta non attiva o con la guida completata restituisce 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= serve solo quando una offerReference appartiene a più richieste del profilo (altrimenti 409 ambiguousOfferReference).
Webhook della guida
Il webhook del programma (vedi Webhook) riceve gli eventi della guida senza un'iscrizione separata. Firma, consegna e tentativi ripetuti sono gli stessi degli altri eventi; data contiene identifier, offerReference, externalApplicationId, guideStatus, occurredAt e sequence (l'ordine degli eventi di una singola offerta) - mai le evidenze, i valori dell'offerta o il link di accesso.
| Evento | Quando |
|---|---|
loanGuide.completed |
la persona richiedente ha seguito la guida e confermato l'offerta |
loanGuide.assistanceRequested |
la persona richiedente ha chiesto una spiegazione (chiamatela) |
loanGuide.expired |
il validUntil dell'offerta è trascorso |
loanGuide.superseded |
una nuova offerta l'ha sostituita (supersedes) |
loanGuide.cancelled |
avete annullato l'offerta |
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
}
}
Ogni evento arriva a un webhook al massimo una volta (chiave di idempotenza = X-Responsiblo-Delivery). Un webhook creato in seguito non riproduce gli eventi precedenti e gli eventi più vecchi di 28 giorni non vengono più comunicati.
Conservazione delle evidenze
Quando il percorso di un'offerta si conclude (offerta completata, annullata, scaduta o sostituita), decorre il periodo di conservazione delle impostazioni della vostra guida (per impostazione predefinita 30 giorni). Poi le evidenze vengono eliminate: GET …/evidence restituisce 410 evidenceErased e lo stato continua a rispondere con evidenceErased: true, i riferimenti e i momenti. Un'offerta attiva non viene mai eliminata. Scaricate le evidenze che dovete conservare subito dopo loanGuide.completed.
Errori della guida
Gli errori hanno la forma dell'API descritta sopra. In più per la guida:
| HTTP | reason | Che cosa fare |
|---|---|---|
| 404 | offerNotFound |
il profilo non ha un'offerta del genere (userNotFound = il profilo non esiste) |
| 409 | offerConflict, activeOfferExists, notSupersedable |
i riferimenti o l'Idempotency-Key appartengono già a un altro corpo, oppure il profilo ha un'altra offerta attiva - inviate supersedes |
| 409 | loanGuideNotConfigured |
la guida non è attivata per il vostro programma - scriveteci |
| 409 | ambiguousOfferReference |
aggiungete ?externalApplicationId= |
| 409 | notActive |
l'offerta non è attiva o ha la guida completata |
| 410 | evidenceErased |
le evidenze sono state eliminate dopo il periodo di conservazione, lo stato resta |
| 422 | invalidOffer |
errors = {percorso: codice[,codice]} con il percorso nel corpo (repayment.schedule[2].date, documents[0].url); i codici nella tabella qui sotto |
| 422 | invalidBody |
supersedes, Idempotency-Key o il corpo dell'annullamento |
errors | Codici dell'errore invalidOffer |
|---|---|
required |
il campo manca |
invalidFormat |
forma errata (tipo, schema, lunghezza, link senza HTTPS o con credenziali) |
outOfRange |
fuori dall'intervallo consentito (importo, numero di documenti, di rate o di giorni) |
notEqualPrincipal |
l'importo erogato non è uguale all'importo del prestito |
sumMismatch |
la somma dei pagamenti non è uguale all'importo totale dovuto |
belowPrincipal |
l'importo totale dovuto è inferiore all'importo del prestito |
aprContradiction |
TAEG 0 con costi positivi |
typeMismatch |
il tipo di rimborso non corrisponde al prodotto impostato per la vostra guida |
missingRole |
manca un documento con un ruolo richiesto dalle impostazioni della vostra guida |
hostNotAllowed |
il link di un documento punta a un host che le impostazioni della vostra guida non consentono |
notAscending |
le date delle rate non sono in ordine crescente o si ripetono |
inPast |
la data di una rata o validUntil è nel passato |
tooFar |
validUntil è più di 7 giorni avanti |
unsupported |
valore non supportato (locale, currency, type, period, kind) |
unknownField |
un campo che l'API non conosce |
looksLikeEmail |
il riferimento sembra un indirizzo e-mail |
Un errore non ripete mai un valore che avete inviato. Esempio: la stessa offerta con un pagamento di 10.700 CZK invece di 10.800 CZK e una validità di 9 giorni:
"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"
}
}
Limiti
120 chiamate al minuto per chiave. Dopo 30 chiavi rifiutate da uno stesso indirizzo in dieci minuti, l'indirizzo attende 10 minuti.
Conserviamo il registro delle chiamate (senza corpi, l'identificativo solo come impronta SHA-256) per 180 giorni, i messaggi dei webhook consegnati e abbandonati per 30 giorni.
Stato e contratto
GET /v1/status- senza chiave, restituisce {"status":"ok","version":"1.3.2"}GET /openapi- senza chiave, il documento OpenAPI 3 dell'API per partner
Sandbox
La chiave di test rsb_test_... apre il vostro programma di test con lo stesso corso. Le persone che vi partecipano terminano dopo 7 giorni; il webhook del programma di test è separato da quello di produzione.
Cronologia delle versioni
- 1.3.2 (2026-11) - Ogni operazione del documento OpenAPI ha un nome e una descrizione, e le operazioni sono suddivise in gruppi. /v1/status e i webhook indicano la versione 1.3.2. La forma delle richieste e delle risposte resta invariata.
- 1.3.1 (2026-10) - Correzioni senza modifiche agli endpoint e alle loro risposte.
- 1.3.0 (2026-10) - Guida al prestito: POST /v1/users/{identifier}/loan-offers, stato, evidenze e annullamento di un'offerta, webhook loanGuide.*, conservazione delle evidenze. Campo facoltativo returnUrl di un'offerta (indirizzo di ritorno proprio).
- 1.2.0 (2026-10) - GET /v1/users con paginazione, il certificato della persona (campo certificate, GET /v1/users/{identifier}/certificate, evento certificate.issued), DELETE /v1/users/{identifier} (anonimizzazione), identificativo di al massimo 64 caratteri, conservazione del registro.
- 1.1.0 (2026-10) - Chiavi API, POST /v1/users, GET /v1/users/{identifier} con ?include=detail, link di accesso, webhook, un'unica forma degli errori, stato pubblico e OpenAPI, limiti.
- 1.0.0 (2024) - GET /v1/user/{identifier} con il JWT della chiave partner.
Volete l'API per la vostra azienda? Scriveteci