Connexions bancaires
Une connexion bancaire (bank_connection) relie une entreprise de votre workspace à sa banque, via une page de consentement hébergée par notre agrégateur. Une connexion n'est pas un compte bancaire : les comptes apparaissent sous elle une fois la synchronisation passée. Aucun endpoint de cette page n'émet quoi que ce soit vers le réseau réglementaire - ni PPF (Portail Public de Facturation), ni Peppol. L'ouverture d'une session de consentement, elle, appelle bien notre agrégateur : c'est tout l'objet de l'appel.
La contrainte à connaître avant de commencer
Il n'existe pas de connexion bancaire sans navigateur. Une intégration serveur à serveur ne peut pas en établir une seule : à un moment, un humain doit ouvrir la page de consentement de l'agrégateur, choisir sa banque et s'authentifier auprès d'elle. L'API ouvre cette page et vous en donne l'adresse ; elle ne la traverse pas à votre place.
C'est une contrainte de l'agrégateur et des banques, pas une limite de Scribee, et elle ne se contourne pas.
Ce que Scribee fait pour vous
- Ouvre la session de consentement auprès de l'agrégateur et vous rend l'adresse à faire ouvrir.
- Ne fait avancer
statusque sur une preuve durable - un événement reçu de l'agrégateur ou une synchronisation explicite. Le retour du navigateur sur votre site n'en est pas une : c'est un signal de navigation, que n'importe qui peut rejouer. - Empêche qu'un appel rejoué ouvre une seconde session, via
Idempotency-Key. - Ne publie jamais les identifiants de l'agrégateur : ils restent internes.
Les endpoints
GET/POST /api/v1/workspaces/{workspace_id}/companies/{company_id}/bank_connections- lister et ouvrirGET /api/v1/bank_connections/{id}- lire une connexion
Comme pour les journaux comptables (Journaux comptables), seules la liste et la création sont adressées par workspace et par entreprise. GET /api/v1/bank_connections/{id} n'a pas de workspace_id dans le chemin : l'identifiant est résolu sur l'ensemble des workspaces rattachés à votre client OAuth.
Une connexion porte dix champs, tous présents dans chaque réponse : id, company_id, status, origin, bank_name, last_synced_at, consent_url, consent_url_expires_at, created_at et updated_at.
last_synced_at est la plus récente de deux dates : la dernière lecture de l'état de la connexion auprès de l'agrégateur, et la dernière synchronisation de l'entreprise arrivée à son terme, complète ou partielle. Une synchronisation en échec ne l'horodate pas à son terme, mais la lecture de l'état de la connexion par laquelle elle commence peut néanmoins l'avoir fait avancer, lorsque cette lecture a elle-même abouti. Il ne bouge pas tant que la connexion est disconnected. Il vaut null tant qu'aucune de ces deux choses n'a eu lieu.
status prend l'une de ces cinq valeurs :
status | Ce qu'il signifie |
|---|---|
pending | La session de consentement est ouverte, personne ne l'a encore menée à son terme |
connected | Une preuve durable confirme l'accès à la banque |
disconnected | L'accès a été révoqué ; l'historique déjà importé est conservé |
expired | L'authentification auprès de la banque a expiré et doit être refaite |
error | L'agrégateur signale un problème sur cette connexion |
Étape 1 : ouvrir une session de consentement
Cet appel s'exécute sur votre workspace de production - il n'y a pas de sandbox. Un token portant le scope write est requis, ainsi qu'un en-tête Idempotency-Key.
user_email est obligatoire : c'est l'adresse de la personne qui va ouvrir la page de consentement. L'agrégateur refuse d'ouvrir une session sans elle, et il peut demander à cette personne de la valider pendant le parcours - envoyez donc l'adresse de l'humain que vous allez rediriger, pas une boîte générique. Nous ne pouvons pas la déduire : sur un flux partenaire, il n'y a aucun utilisateur connecté chez nous, et le membre du workspace qui détient l'entreprise n'est pas celui qui est devant l'écran.
redirect_url est facultative. Elle indique où Scribee vous renvoie le navigateur une fois la session terminée ; elle doit figurer parmi les URI de redirection enregistrées pour votre application OAuth, faute de quoi l'appel est refusé.
L'agrégateur, lui, renvoie le navigateur vers Scribee. C'est ce détour qui nous permet d'enregistrer l'issue de la session sur la connexion dont vous venez de recevoir l'id, avant de vous rendre la main. Vous n'avez rien à faire de cette étape, sinon savoir qu'elle existe.
curl -X POST https://app.scribee.tech/api/v1/workspaces/YOUR_WORKSPACE_ID/companies/YOUR_COMPANY_ID/bank_connections \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Idempotency-Key: 4f1c2a6e-0d1b-4f2a-9a11-8d0e2c3b7a55" \
-H "Content-Type: application/json" \
-d '{
"user_email": "consenting.user@example.com",
"redirect_url": "https://partner.example.com/banking/return"
}'
Réponse 201 :
{
"data": {
"id": 501,
"company_id": 34,
"status": "pending",
"origin": "synchronized",
"bank_name": null,
"last_synced_at": null,
"consent_url": "https://connect.example-provider.com/s/abc123",
"consent_url_expires_at": "2026-08-21T11:30:00+02:00",
"created_at": "2026-08-21T11:15:00+02:00",
"updated_at": "2026-08-21T11:15:00+02:00"
}
}
Ce 201 signifie qu'une session existe, jamais qu'une banque est connectée. La connexion est en pending et le restera jusqu'à preuve durable.
consent_url est à usage unique et de courte durée. Ne la mettez pas en cache, ne la journalisez pas, ne la re-servez pas à un autre utilisateur.
consent_url_expires_at porte l'échéance que nous appliquons, et il faut la traiter comme telle. Passé cette date, la page peut encore s'ouvrir, mais un parcours terminé après elle ne peut plus faire avancer cette connexion : prévenez l'utilisateur ou ouvrez une nouvelle session plutôt que d'attendre. L'agrégateur, lui, ne renvoie aucune date d'expiration - nous publions la nôtre, pas une supposition sur la sienne.
Quand la réponse est un 202
Il arrive que l'agrégateur réponde sans que sa réponse tranche : un dépassement de délai, une erreur de son côté, ou un succès qui ne porte aucune adresse. Nous ne savons alors pas si une session a été ouverte, et vous répondre 201 ou vous opposer un refus serait dans les deux cas une affirmation que nous ne pouvons pas tenir.
Vous recevez donc un 202 portant la connexion, sans consent_url - il n'y en a pas à donner. Ce que vous en faites :
- interrogez l'
id. Si une session existait bel et bien et que la personne la mène à son terme, c'est cette connexion-là qui avancera ; - si elle est toujours en
pendingpasséconsent_url_expires_at, ouvrez une nouvelle session avec une nouvelleIdempotency-Key.
Rejouer la même clé vous rend cette même réponse au lieu d'ouvrir une seconde session.
Reprendre une connexion : reconnect
Une connexion passe en expired quand l'authentification auprès de la banque arrive à échéance, ou en error quand l'agrégateur signale un problème. Dans les deux cas, la reprise passe par une nouvelle session de consentement :
curl -X POST https://app.scribee.tech/api/v1/bank_connections/501/reconnect \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Idempotency-Key: 9b2e1f04-7c3a-4d15-b8e6-2a5c7d9e1f30" \
-H "Content-Type: application/json" \
-d '{
"redirect_url": "https://partner.example.com/banking/return"
}'
Vous récupérez la connexion avec une consent_url neuve, à faire ouvrir par un humain exactement comme la première fois.
Deux différences avec la création, et elles comptent :
- le
statusne bouge pas. Une nouvelle session est une intention, pas une preuve : la connexion reste dans l'état où la dernière lecture l'a laissée jusqu'à ce qu'un événement ou une synchronisation dise autre chose ; - aucun
user_emailn'est attendu. Le parcours de reprise de l'agrégateur identifie l'item, pas une personne.
Une connexion qui n'a jamais atteint l'agrégateur - restée en pending sans qu'aucune session n'aboutisse - n'a rien à reprendre : l'appel est refusé en invalid_argument. Ouvrez une nouvelle connexion à la place.
Débrancher une connexion : DELETE
curl -X DELETE https://app.scribee.tech/api/v1/bank_connections/501 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Ce n'est pas un endpoint de suppression de données. Le status passe à disconnected, l'accès de l'agrégateur à la banque s'arrête, et tout ce qui a déjà été importé est conservé : comptes, opérations, écritures. La connexion elle-même reste lisible.
Un token portant le scope destroy suffit ; write est également accepté.
Pour reprendre plus tard, reconnect rouvre une session sur cette même connexion.
Étape 2 : faire ouvrir la page par un humain
Envoyez l'utilisateur sur consent_url. Il y choisit sa banque et s'authentifie. À la fin, l'agrégateur renvoie le navigateur vers Scribee, qui enregistre l'issue de la session puis le renvoie vers votre redirect_url si vous en avez fourni une.
Ce retour ne prouve rien. Il ne porte aucune autorité : ne l'utilisez pas pour afficher un succès, ni pour déclencher un traitement. Servez-vous-en seulement pour ramener l'utilisateur dans votre interface. Ce qu'il garantit, c'est que la connexion dont vous détenez l'id est bien celle que la suite fera avancer - pas qu'elle est connectée.
Étape 3 : interroger la connexion
C'est ici que vous apprenez ce qui s'est réellement passé. Un token read suffit.
curl https://app.scribee.tech/api/v1/bank_connections/501 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
status passe à connected uniquement lorsqu'un événement de l'agrégateur ou une synchronisation l'a confirmé. Tant qu'il vaut pending, la connexion n'est pas établie - que l'utilisateur soit revenu sur votre site ou non.
bank_name porte le nom de la banque une fois que nous avons pu rattacher la connexion à la sienne, et vaut null tant que ce rattachement n'a pas eu lieu. Deux cas ordinaires le laissent à null : la banque choisie ne figure pas encore dans notre catalogue, qui est un instantané de ce que publie l'agrégateur et non une garantie ; ou l'agrégateur ne nous a rien dit de la banque de cette connexion. Prévoyez donc null y compris sur une connexion connected : ce champ ne dit rien de l'état de la connexion, c'est status qui le porte.
consent_url vaut null sur cette réponse : elle n'est renseignée qu'à l'ouverture d'une session.
Étape 4 : lister les connexions d'une entreprise
curl "https://app.scribee.tech/api/v1/workspaces/YOUR_WORKSPACE_ID/companies/YOUR_COMPANY_ID/bank_connections?status=connected" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
La liste est rendue de la plus récente à la plus ancienne et se pagine avec page et per_page. Le filtre status accepte l'une des cinq valeurs ci-dessus ; toute autre valeur ne remonte simplement rien.
include=bank_accounts ajoute à chaque connexion la liste des comptes qui en dépendent, chacun avec toute sa charge utile - la même que celle décrite dans Comptes bancaires. Sans ce paramètre, la clé est absente.
Les erreurs
| Statut | code | Quand |
|---|---|---|
422 | validation_failed | L'en-tête Idempotency-Key ou le champ user_email manque |
422 | invalid_argument | L'user_email fourni n'est pas une adresse valide, ou la redirect_url fournie n'est pas enregistrée pour votre application |
422 | provider_quota_exceeded | L'agrégateur refuse d'ouvrir une connexion de plus pour ce compte |
422 | operation_failed | L'agrégateur a refusé la session pour un motif transitoire, ou une autre session est déjà en cours d'ouverture pour la même entreprise - réessayez |
409 | idempotency_key_reuse | La même Idempotency-Key a déjà servi pour un corps différent |
409 | idempotency_request_in_progress | Un appel antérieur portant cette clé est encore en cours |
403 | - | Votre client OAuth n'a pas d'habilitation sur ce workspace |
404 | - | L'entreprise ou la connexion n'est pas atteignable par vos habilitations |
Les deux 409 ne se réessaient pas de la même façon. idempotency_key_reuse demande une clé neuve - ou le corps d'origine, si vous vouliez rejouer la réponse mémorisée. idempotency_request_in_progress reste refusé jusqu'à l'expiration de la clé : c'est un signal pour aller vérifier ce qu'a fait le premier appel, jamais une invitation à boucler.
provider_quota_exceeded nomme une action de votre côté : libérer une connexion existante ou changer d'offre auprès de nous. Réessayer à l'identique sera refusé de nouveau.