Aller au contenu principal

Lots de paiement

Un lot de paiement (payment_batch) regroupe des virements fournisseurs débités d'un même compte bancaire et approuvés ensemble. Chaque virement du lot est une instruction de paiement (payment_instruction) : un bénéficiaire, un montant, une date d'exécution.

Cette API prépare les lots - création d'un brouillon, modification, soumission à approbation, approbation ou refus -, remet un lot hosted_consent approuvé à son canal, génère le fichier SEPA d'un lot sepa_file approuvé, lit les lots et leurs instructions, et vous laisse rapprocher chaque instruction de l'opération bancaire qui l'a réglée. Aucune de ces opérations ne prouve qu'un virement a été exécuté : seuls l'état du lot et le statut de ses instructions en disent quelque chose.

Ce que cette surface publie​

Vingt opérations :

Verbe et cheminCe qu'elle fait
GET /api/v1/workspaces/{workspace_id}/companies/{company_id}/payment_batchesListe les lots de l'entreprise
GET /api/v1/payment_batches/{id}Lit un lot
GET /api/v1/payment_batches/{id}/instructionsListe les instructions d'un lot
GET /api/v1/payment_instructions/{id}Lit une instruction
GET /api/v1/payment_batches/{id}/settlementLit le règlement des instructions d'un lot et ses suggestions
GET /api/v1/payment_batches/{id}/settlement/candidatesCherche les opérations bancaires qui ont pu régler une instruction, sur les dates de votre choix
POST /api/v1/workspaces/{workspace_id}/companies/{company_id}/payment_batchesCrée un lot en brouillon
PATCH /api/v1/payment_batches/{id}Modifie un brouillon
POST /api/v1/payment_batches/{id}/submit_for_approvalSoumet un brouillon à approbation
POST /api/v1/payment_batches/{id}/approveApprouve un lot
POST /api/v1/payment_batches/{id}/refuseRefuse l'approbation d'un lot
POST /api/v1/payment_batches/{id}/submitRemet un lot hosted_consent approuvé à son canal
POST /api/v1/payment_batches/{id}/consent_sessionPublie l'adresse de la page de consentement d'un lot hosted_consent remis
POST /api/v1/payment_batches/{id}/settlement/suggestions/{suggestion_id}/dismissÉcarte une suggestion
POST /api/v1/payment_batches/{id}/settlement/suggestions/{suggestion_id}/confirmConfirme une suggestion
POST /api/v1/payment_batches/{id}/settlement/confirmConfirme l'opération bancaire qui a réglé une instruction
POST /api/v1/payment_batches/{id}/settlement/instructions/{payment_instruction_id}/undoAnnule le rapprochement d'une instruction
POST /api/v1/payment_batches/{id}/settlement/instructions/{payment_instruction_id}/not_executedDéclare non exécutée une instruction dont l'exécution est à confirmer
POST /api/v1/payment_batches/{id}/sepa_exportGénère le fichier SEPA d'un lot
GET /api/v1/payment_batches/{id}/sepa_exportLit l'export d'un lot, ou télécharge son fichier

Un token portant le scope read est requis sur les six premières. Toutes les autres exigent le scope write : les sept écritures sur les lots, les cinq opérations POST sur settlement et les deux opérations sur sepa_export, y compris leur GET.

Lister les lots d'une entreprise​

curl "https://app.scribee.tech/api/v1/workspaces/YOUR_WORKSPACE_ID/companies/YOUR_COMPANY_ID/payment_batches?state=submitted&per_page=50" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Les lots sortent du plus récemment créé au plus ancien, à égalité de date de l'identifiant le plus grand au plus petit. La pagination est par décalage, avec page et per_page - 20 par défaut, 100 au maximum - et meta porte current_page, per_page, total_pages et total_count.

Cinq filtres, tous facultatifs et cumulables :

FiltreValeursCe qu'il retient
stateune valeur de stateLes lots dans cet état
channelhosted_consent, sepa_fileLes lots remis par ce canal
bank_account_idun entierLes lots débités de ce compte
execution_date_fromune date AAAA-MM-JJLes lots dont l'execution_date est à cette date ou après
execution_date_toune date AAAA-MM-JJLes lots dont l'execution_date est à cette date ou avant

Un filtre que nous ne savons pas appliquer rend une collection vide, jamais une erreur : une valeur de state ou de channel inconnue, un identifiant qui n'est pas un entier, une date dans un autre format que AAAA-MM-JJ. Un paramètre absent ou vide n'est pas un filtre. Un lot sans execution_date n'est retenu par aucun des deux filtres de date.

Lire un lot​

curl https://app.scribee.tech/api/v1/payment_batches/7301 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
{
"data": {
"id": 7301,
"company_id": 34,
"bank_account_id": 812,
"name": "Fournisseurs 2026-08",
"channel": "hosted_consent",
"state": "submitted",
"version": 6,
"instructions_count": 2,
"total_amount": 1250.5,
"currency_code": "EUR",
"execution_date": "2026-08-29",
"approved_at": "2026-08-27T10:02:11+02:00",
"submitted_at": "2026-08-27T10:05:40+02:00",
"progress": null,
"error_code": null,
"error_message": null,
"retryable": false,
"started_at": "2026-08-27T10:05:40+02:00",
"finished_at": "2026-08-27T10:05:43+02:00",
"submission_status": "accepted",
"submission_refused_by": null,
"verification_required": false,
"approved_by": {
"actor_type": "api_client",
"name": "Compta Connect",
"decided_at": "2026-08-27T10:02:11+02:00"
},
"refused_by": null,
"created_at": "2026-08-26T16:40:02+02:00",
"updated_at": "2026-08-27T10:05:43+02:00"
}
}

channel dit par où le lot quitte Scribee : hosted_consent, une demande de paiement validée par un consentement donné sur une page de la banque ; sepa_file, un fichier de virements SEPA produit par Scribee.

version change à chaque écriture du lot, y compris quand l'une de ses instructions est modifiée. Une exception : le passage d'une instruction à pending, settled ou rejected ne change pas version à lui seul ; seul le changement d'état du lot qui peut en découler le fait.

started_at porte la même valeur que submitted_at. progress vaut toujours null : rien ne mesure l'avancement d'une remise, et nous ne publions pas de pourcentage fabriqué.

Les états d'un lot​

stateCe qu'il signifie
draftLe lot est en préparation. Un lot dont l'approbation a été refusée revient ici
pending_approvalLe lot attend la décision d'une personne habilitée à l'approuver
approvedLe lot est approuvé. Sa remise n'a pas commencé, ou elle a commencé et son issue n'est pas encore connue
submittedLe lot a été remis à son canal. L'issue d'au moins une de ses instructions n'est pas connue
completedChaque instruction du lot est settled
partially_completedCertaines instructions du lot sont settled et les autres rejected : le status de chaque instruction dit lesquelles
rejectedChaque instruction du lot est rejected. Cet état est définitif
failedLa remise du lot a été refusée, par le prestataire de paiement ou par Scribee avant tout envoi : submission_refused_by dit lequel. Cet état est définitif

Deux états sont définitifs : rejected et failed. completed et partially_completed n'ont qu'une sortie : l'annulation d'un règlement que seule une confirmation portait, qui ramène le lot à submitted (Annuler un règlement confirmé).

submitted ne prouve pas que les virements ont été exécutés. Il dit que le canal a pris le lot : pour hosted_consent, le canal a accepté la demande de paiement ; pour sepa_file, le fichier a été produit. Le lot ne passe à completed, partially_completed ou rejected que lorsque chacune de ses instructions a un statut définitif : tant que l'une d'elles est submitted ou pending, le lot reste submitted. Une instruction devient settled quand la banque signale son exécution, ou quand vous confirmez l'opération bancaire qui l'a réglée. Scribee ne reçoit pas de réponse de la banque sur un fichier SEPA : un lot sepa_file ne quitte submitted que par la confirmation du règlement de ses instructions, ou par la déclaration de non-exécution de celles dont l'exécution est à confirmer.

Une remise engagée dont l'issue n'est pas connue​

Un lot approved dont submitted_at est renseigné n'est pas un lot en attente de remise. La remise est engagée, et son issue n'est pas encore connue : un consentement bancaire qui n'a pas encore été donné, un fichier SEPA en cours de génération, ou un canal qui n'a pas répondu à temps. finished_at reste null tant que le canal n'a pas donné de réponse définitive.

Tant que le lot reste dans cette situation, ses instructions ne peuvent pas être remises une seconde fois. Interrogez le lot jusqu'à ce qu'il quitte approved, ou attendez l'événement payment_batch.updated qui annonce chaque changement d'état : il passe à submitted, puis à completed, partially_completed ou rejected si la réponse du canal porte déjà l'issue de chaque instruction, ou à failed.

Où en est la remise : submission_status​

state reste approved tant que la remise engagée n'a pas d'issue. submission_status dit laquelle de ces situations le lot traverse :

submission_statusCe qu'il signifie
noneAucune remise n'a été engagée
awaiting_consentLa demande de paiement a été créée ; le titulaire du compte ne l'a pas encore autorisée sur la page de sa banque
in_progressLe canal produit encore la remise, par exemple un fichier SEPA en cours de génération
unknownAucune réponse exploitable du canal n'est arrivée
acceptedLe canal a pris le lot
refusedLa remise a été refusée, par le prestataire ou par Scribee avant tout envoi : le lot est failed, et submission_refused_by dit par qui

Un lot sepa_file dont la remise est engagée lit in_progress tant que son fichier est en cours de génération, et unknown sinon, jusqu'à ce qu'une issue soit enregistrée.

Ni awaiting_consent, ni in_progress, ni unknown ne signifie que le paiement est annulé ou refusé. Le lot reste approved, un paiement peut encore avoir lieu, et Scribee refuse toute nouvelle remise de ce lot tant que c'est le cas. Une page de consentement fermée, un retour sans succès ou une attente prolongée ne prouvent ni une annulation ni un refus : seule une réponse du canal fait sortir le lot de approved.

Une vérification à faire : verification_required​

verification_required vaut true quand un lot hosted_consent a sa remise awaiting_consent ou unknown et qu'elle a été engagée il y a 24 heures ou plus (submitted_at). Il vaut toujours false sur un lot sepa_file. Il repasse à false dès que chacune des instructions du lot est settled et porte un bank_operation_id : la banque montre déjà l'argent sorti du compte, il n'y a plus rien à vérifier. C'est une simple lecture : l'état du lot ne change pas, rien n'est libéré, et le prestataire reste interrogé pour son statut définitif. C'est une indication, pas une conclusion : le paiement doit être vérifié auprès du prestataire, et le lot n'a été ni annulé ni refusé. Scribee n'en tire aucun changement d'état, ne libère rien, et continue de refuser une nouvelle remise.

verification_required est calculé à chaque lecture. Il peut passer à true sans changement d'état, donc sans événement payment_batch.updated : lisez le lot pour le connaître.

Quand une remise échoue​

error_code vaut operation_failed dès que la dernière tentative de remise porte un diagnostic, et null sinon. error_message est alors une phrase rédigée par Scribee, jamais le texte brut renvoyé par le canal.

Qui a refusé la remise : submission_refused_by​

Un lot ne passe à failed que lorsque sa remise engagée a été refusée de façon certaine. submission_refused_by dit par qui :

submission_refused_byCe qu'il signifie
providerLe prestataire de paiement a renvoyé un refus pour le lot
localScribee a refusé le lot avant tout envoi : le prestataire ne l'a jamais reçu, et aucun résultat bancaire n'est enregistré. Par exemple un lot incomplet, ou un fichier SEPA qui échoue à sa validation
nullLe lot n'a pas été refusé, ou il l'a été avant que cette information soit enregistrée : l'origine du refus n'est pas connue

error_message donne la raison du refus. Le refus du lot, à lui seul, n'enregistre aucun résultat bancaire pour ses instructions : il ne confirme ni n'exclut un rejet ou une exécution à la banque. Seule une instruction que la réponse du prestataire signale elle-même comme refusée passe à rejected (Le statut d'une instruction). Un lot failed n'est jamais remis une seconde fois.

error_code peut être renseigné sur un lot approved. Il décrit la dernière tentative - un canal qui n'a pas répondu, par exemple - et non l'issue du lot. Seul l'état failed dit que la remise a échoué. rejected dit autre chose : la remise a abouti, et la banque a refusé chaque instruction.

retryable dit si une nouvelle tentative peut plausiblement aboutir autrement. Il vaut false sur un lot qui n'a jamais été remis.

Être notifié d'un changement d'état​

Plutôt que d'interroger les lots en boucle, abonnez un endpoint webhook à payment_batch.updated (Webhooks). L'événement est émis à chaque changement d'état d'un lot, et à ce seul moment ; il porte le nouvel état dans state et l'état quitté dans previous_state. Les dix transitions qui l'émettent, et les huit clés de son corps, sont décrites sur la page Webhooks.

Le changement du status publié d'une instruction est annoncé à part, par payment_instruction.updated, qui porte le statut quitté dans previous_status (Webhooks).

Les instructions d'un lot​

curl https://app.scribee.tech/api/v1/payment_batches/7301/instructions \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Les instructions sortent dans l'ordre croissant de leur identifiant, paginées comme les lots. Une instruction se lit aussi seule, par GET /api/v1/payment_instructions/{id} :

{
"data": {
"id": 61004,
"payment_batch_id": 7301,
"company_id": 34,
"invoice_document_id": 88120,
"beneficiary_name": "Papeterie Martin SARL",
"beneficiary_iban_masked": "FR*********************0189",
"beneficiary_iban_last4": "0189",
"amount": 830.0,
"currency_code": "EUR",
"execution_date": "2026-08-29",
"reference": "FA-2026-0310",
"end_to_end_id": "SCB-6100-78010",
"status": "submitted",
"created_at": "2026-08-26T16:41:15+02:00",
"updated_at": "2026-08-26T16:41:15+02:00"
}
}

invoice_document_id désigne la facture que l'instruction règle, quand elle en règle une ; il vaut null sinon. end_to_end_id est l'identifiant de bout en bout du virement, unique, 35 caractères au plus.

L'IBAN du bénéficiaire n'est jamais publié en entier. beneficiary_iban_masked en garde les deux premiers caractères et les quatre derniers, et remplace tous les autres par * ; beneficiary_iban_last4 porte ces quatre derniers caractères. Une valeur de moins de huit caractères est entièrement masquée, et son beneficiary_iban_last4 vaut null.

Le statut d'une instruction​

statusCe qu'il signifie
draftLe lot de l'instruction n'a pas engagé de remise
submittedLe lot de l'instruction a engagé une remise, et la banque n'a pas donné de réponse sur cette instruction
submission_failedLa remise du lot a été refusée, et aucun résultat bancaire n'est enregistré pour cette instruction. Le lot est failed et n'est pas remis de nouveau
pendingLa banque a accepté l'instruction et ne l'a pas encore exécutée. Certaines banques ne confirment jamais au-delà : ne la traitez pas comme payée
settledLa banque a signalé l'exécution de l'instruction, ou une personne a confirmé l'opération bancaire qui l'a réglée
rejectedLa banque a refusé l'instruction, ou une personne a déclaré qu'elle n'a pas été exécutée. Ce statut est définitif

draft, submitted et submission_failed ne disent rien de l'issue de l'instruction à la banque ; pending, settled et rejected en disent quelque chose. Approuver un lot ne change pas ses instructions : elles restent draft jusqu'à ce que le lot engage sa remise. Une instruction passe à submitted au moment où son lot engage sa remise. Elle y reste tant que l'issue de la remise n'est pas connue - consentement en attente, fichier SEPA en cours de génération, réponse du canal perdue -, comme sur un lot sepa_file dont aucun règlement n'a été confirmé. Elle passe à submission_failed quand le lot passe à failed, que le refus vienne du prestataire ou de Scribee avant tout envoi ; une instruction qui porte déjà un statut de la banque garde le sien. Une exception : quand la réponse du prestataire qui refuse la remise signale elle-même une instruction du lot comme refusée, cette instruction passe à rejected, et non à submission_failed. Les autres instructions du lot passent à submission_failed ; un lot failed peut donc porter les deux statuts, que le filtre status sépare. L'API ne publie pas le motif de ce refus. Sur un lot partially_completed, chaque instruction est settled ou rejected.

submission_failed n'est pas un résultat bancaire enregistré pour cette instruction : il ne confirme ni n'exclut un rejet ou une exécution à la banque - voyez la raison du refus du lot (error_message). Pour savoir qui a refusé la remise, lisez submission_refused_by sur le lot (Qui a refusé la remise).

settled ne se quitte que dans un cas : l'annulation d'un règlement confirmé, sur une instruction dont la banque n'a pas signalé l'exécution. L'instruction revient alors à pending.

Ni l'état d'un lot ni le statut d'une instruction n'enregistrent le paiement d'une facture. Sur cette surface, seule la confirmation d'un règlement l'enregistre.

Deux filtres, facultatifs et cumulables, sur la liste des instructions d'un lot :

FiltreValeursCe qu'il retient
statusdraft, submitted, submission_failed, pending, settled, rejectedLes instructions qui publient ce statut
invoice_document_idun entierLes instructions qui règlent cette facture

Comme sur les lots, un filtre que nous ne savons pas appliquer rend une collection vide.

Les suggestions de règlement d'un lot​

curl https://app.scribee.tech/api/v1/payment_batches/7301/settlement \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Cette lecture rend une ligne par instruction du lot, dans l'ordre croissant de payment_instruction_id. Elle n'est pas paginée et ne porte pas de clé meta : la collection est bornée par les instructions du lot.

{
"data": [
{
"payment_instruction_id": 61004,
"state": "matched",
"requires_review": false,
"bank_operation_id": 30401,
"invoice_payment_id": 51120,
"execution_to_confirm_at": null,
"not_executed_declaration": null,
"provider_evidence_conflict_at": null,
"late_provider_events": [],
"candidate_bank_operation_ids": [],
"matched_on": ["amount"],
"suggestions": []
},
{
"payment_instruction_id": 61005,
"state": "ambiguous",
"requires_review": true,
"bank_operation_id": null,
"invoice_payment_id": null,
"execution_to_confirm_at": null,
"not_executed_declaration": null,
"provider_evidence_conflict_at": null,
"late_provider_events": [],
"candidate_bank_operation_ids": [30402],
"matched_on": ["amount"],
"suggestions": [
{
"id": 91204,
"payment_instruction_id": 61005,
"bank_operation_id": 30402,
"state": "proposed",
"amount_match": "exact",
"day_offset": 2,
"window_start": "2026-08-24",
"window_end": "2026-09-08",
"explanation": "Outgoing operation of the same amount and currency booked 2 days after the planned execution date (2026-08-29), within the review window 2026-08-24 to 2026-09-08."
}
]
},
{
"payment_instruction_id": 61006,
"state": "unmatched",
"requires_review": false,
"bank_operation_id": null,
"invoice_payment_id": null,
"execution_to_confirm_at": null,
"not_executed_declaration": null,
"provider_evidence_conflict_at": null,
"late_provider_events": [],
"candidate_bank_operation_ids": [],
"matched_on": [],
"suggestions": []
}
]
}

Une suggestion est une opération bancaire qui ressemble au débit d'une instruction : une opération sortante du compte débité par le lot, dans la même devise, d'un montant exactement égal à celui de l'instruction, et datée dans la fenêtre d'examen. Une opération déjà rapprochée de factures au moment du calcul n'est pas suggérée.

Une suggestion ne prouve ni le règlement de l'instruction, ni le paiement de la facture. Elle est proposée à l'examen d'une personne, et rien de plus : elle ne change pas le status de l'instruction ni l'état du lot, et n'enregistre aucun paiement de facture. Seule une confirmation le fait (Confirmer le règlement d'une instruction).

Lire une ligne​

CléCe qu'elle porte
stateunmatched quand aucune opération n'est suggérée ; ambiguous quand une ou plusieurs le sont et qu'une personne doit les examiner ; matched quand l'opération qui a réglé l'instruction a été confirmée
requires_reviewtrue quand state vaut ambiguous, false sinon
bank_operation_idL'opération confirmée ; null tant que state ne vaut pas matched
invoice_payment_idLe paiement de facture que la confirmation a enregistré ou repris ; null tant que state ne vaut pas matched, et sur une instruction qui ne règle aucune facture
execution_to_confirm_atUne date et une heure ISO 8601 quand le règlement confirmé de l'instruction a été annulé sans que la banque ait signalé son exécution : l'instruction est revenue à pending, et son exécution reste à confirmer. null sur toute autre instruction, et de nouveau dès que la banque signale l'exécution ou le rejet de l'instruction, qu'une opération bancaire est confirmée pour elle, ou qu'elle est déclarée non exécutée
not_executed_declarationLa déclaration de non-exécution de l'instruction : reason, note et declared_at, une date et une heure ISO 8601. null sur toute instruction qui n'a pas été déclarée non exécutée, y compris une instruction refusée par la banque
provider_evidence_conflict_atUne date et une heure ISO 8601 quand un compte rendu de la banque, reçu alors que l'instruction était settled, l'a contredite : l'instant de réception du premier. null sinon. Il ne change pas le state de la ligne
late_provider_eventsLes comptes rendus de la banque reçus après coup, conservés sans être appliqués, du plus ancien au plus récent reçu : scheme_status_code, occurred_at et received_at. [] sinon
candidate_bank_operation_idsLes identifiants des opérations suggérées, dans l'ordre croissant
matched_on["amount"] quand au moins une opération est suggérée ou confirmée, [] sinon
suggestionsLe détail de chaque opération suggérée, dans le même ordre ; vide sur une ligne matched

Une seule opération suggérée donne aussi ambiguous : l'API ne choisit jamais un candidat à votre place.

Chaque élément de suggestions porte :

CléCe qu'elle porte
idL'identifiant de la suggestion, que l'écarter ou la confirmer désigne
payment_instruction_idL'instruction concernée
bank_operation_idL'opération suggérée, lisible sur Opérations bancaires
stateToujours proposed : la suggestion attend un examen
amount_matchToujours exact : l'opération porte exactement le montant de l'instruction, dans la même devise
day_offsetLe nombre de jours calendaires entre la date de l'opération et la date de référence de la fenêtre, négatif quand l'opération est antérieure
window_start, window_endLe premier et le dernier jour de la fenêtre d'examen parcourue
explanationUne phrase en anglais qui dit pourquoi l'opération a été suggérée

La fenêtre d'examen​

La fenêtre va de 5 jours avant à 10 jours après une date de référence, en jours calendaires, bornes incluses. La date de référence est l'execution_date de l'instruction, à défaut celle du lot, à défaut le jour de submitted_at. Une opération datée hors de la fenêtre n'est jamais suggérée ; la recherche manuelle la trouve.

La fenêtre filtre les candidats ; elle ne prouve pas le règlement.

Quand les suggestions sont calculées​

Scribee calcule les suggestions en arrière-plan, et non au moment de votre lecture : quand la banque communique un statut pour les instructions du lot, quand de nouvelles opérations bancaires arrivent pour l'entreprise, par synchronisation bancaire ou par un relevé importé, et après l'annulation d'un règlement. Une suggestion n'est calculée que pour une instruction qui n'est pas rejected et n'est pas déjà rapprochée, dans un lot submitted, completed ou partially_completed, ou approved avec un submitted_at renseigné.

Chaque calcul retire les suggestions dont l'opération n'est plus candidate - une instruction devenue rejected, une opération rapprochée de factures entre-temps - et elles disparaissent de la ligne. Seules les suggestions en attente d'examen sont publiées.

Comme pour la lecture d'un lot, un lot que vos habilitations ne couvrent pas répond 404, même si le rapprochement bancaire est désactivé dans son workspace.

Un compte rendu de la banque reçu après coup​

Une fois settled, une instruction ne change plus de statut sur un compte rendu de la banque. Quand la banque en communique un malgré tout sur cette instruction - son règlement a pu être confirmé avant que la banque ait répondu -, Scribee le conserve sans l'appliquer :

  • le status de l'instruction, le state de sa ligne de règlement, son bank_operation_id et son invoice_payment_id ne changent pas ;
  • aucun paiement de facture n'est enregistré ni retiré, et aucun montant de facture n'est réservé ni libéré ;
  • la conservation du compte rendu ne change ni l'état ni la version du lot.

La ligne de règlement publie ces comptes rendus dans late_provider_events, du plus ancien au plus récent reçu. Un compte rendu qui répète le statut et le code que l'instruction porte déjà n'y figure pas, un ACSC non plus : il confirme l'exécution de l'instruction, et il est conservé comme tel (Annuler un règlement confirmé). Un même code n'y figure qu'une fois, même quand la banque le répète à chaque lecture.

CléCe qu'elle porte
scheme_status_codeLe code ISO 20022 que la banque a communiqué
occurred_atL'instant où ce statut a été constaté, une date et une heure ISO 8601 ; null quand il n'est pas connu
received_atL'instant où Scribee a reçu le compte rendu, une date et une heure ISO 8601

Un rejet reçu après le règlement contredit l'instruction. Quand la banque communique un RJCT sur une instruction settled, la ligne de règlement porte provider_evidence_conflict_at, l'instant de réception de ce premier compte rendu contradictoire. Le champ n'est écrit qu'une fois et n'est jamais remis à null : un compte rendu contradictoire suivant s'ajoute à late_provider_events sans le changer. Tout autre code est conservé sans renseigner ce champ.

{
"payment_instruction_id": 61004,
"state": "matched",
"requires_review": false,
"bank_operation_id": 30401,
"invoice_payment_id": 51120,
"execution_to_confirm_at": null,
"not_executed_declaration": null,
"provider_evidence_conflict_at": "2026-09-29T10:00:00+02:00",
"late_provider_events": [
{
"scheme_status_code": "RJCT",
"occurred_at": "2026-09-29T10:00:00+02:00",
"received_at": "2026-09-29T10:00:00+02:00"
}
],
"candidate_bank_operation_ids": [],
"matched_on": ["amount"],
"suggestions": []
}

provider_evidence_conflict_at ne tranche rien. L'instruction reste settled, et le paiement de facture que la confirmation a enregistré reste en place : la contradiction est à examiner par une personne. Si le règlement confirmé se révèle erroné, annulez-le.

Aucun webhook ne signale un compte rendu reçu après coup. Sa conservation ne change pas l'état du lot, et payment_batch.updated n'est émis que sur un changement d'état ; aucun autre événement webhook ne le signale. Pour le détecter, lisez la ligne de règlement : GET /api/v1/payment_batches/{id}/settlement.

Agir sur le règlement d'une instruction​

Cinq opérations transforment ces suggestions en décisions : écarter une suggestion, chercher une opération hors de la fenêtre, confirmer l'opération qui a réglé une instruction, annuler cette confirmation, et déclarer non exécutée une instruction dont l'exécution est à confirmer.

Aucune d'elles n'exécute, n'annule ni ne transmet un virement à la banque. Elles enregistrent dans Scribee ce que vous constatez sur le compte. Confirmer enregistre le paiement de la facture de l'instruction ; annuler retire ce que la confirmation a enregistré, et rien d'autre ; déclarer une non-exécution libère le montant réservé de la facture.

OpérationScopeIdempotency-Key
GET /api/v1/payment_batches/{id}/settlement/candidatesreadsans objet
POST /api/v1/payment_batches/{id}/settlement/suggestions/{suggestion_id}/dismisswriteacceptée, facultative
POST /api/v1/payment_batches/{id}/settlement/suggestions/{suggestion_id}/confirmwriteexigée
POST /api/v1/payment_batches/{id}/settlement/confirmwriteexigée
POST /api/v1/payment_batches/{id}/settlement/instructions/{payment_instruction_id}/undowriteexigée
POST /api/v1/payment_batches/{id}/settlement/instructions/{payment_instruction_id}/not_executedwriteexigée

La clé est exigée sur la confirmation et l'annulation parce qu'elles s'annulent l'une l'autre : rejouer une confirmation après une annulation confirmerait de nouveau. Elle l'est aussi sur la déclaration de non-exécution, qui libère le montant réservé de la facture. Avec la même clé, un rejeu renvoie la réponse enregistrée.

Écarter une suggestion​

curl -X POST https://app.scribee.tech/api/v1/payment_batches/7301/settlement/suggestions/91204/dismiss \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Écarter dit « cette opération n'a pas réglé cette instruction ». La réponse 200 porte la suggestion, dont state vaut désormais dismissed.

Rien d'autre ne change : ni le status de l'instruction, ni l'état du lot, ni la facture, ni la comptabilité. La suggestion disparaît de la ligne, et cette paire n'est plus jamais suggérée. Écarter une suggestion déjà écartée répond le même 200. Une suggestion retirée entre-temps par un calcul ou par une confirmation est refusée par un 422 operation_failed : elle n'est plus devant personne.

Chercher une opération hors de la fenêtre​

curl "https://app.scribee.tech/api/v1/payment_batches/7301/settlement/candidates?payment_instruction_id=61005&from=2026-09-01&to=2026-10-31" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
{
"data": [
{
"bank_operation_id": 30405,
"operation_date": "2026-10-13",
"amount_match": "exact",
"day_offset": 45
}
]
}

Un débit arrivé tard, ou un relevé importé des mois après, tombe hors de la fenêtre d'examen. Cette recherche parcourt les dates que vous choisissez, avec les mêmes contrôles que les suggestions : le compte débité par le lot, une opération sortante, la même devise, le montant exact, et une opération qu'aucune autre instruction ni aucune facture n'a prise.

  • payment_instruction_id, from et to sont exigés ; les dates s'écrivent AAAA-MM-JJ.
  • to n'est pas antérieure à from, et la plage couvre au plus 366 jours, bornes incluses.
  • L'instruction appartient au lot, et une suggestion pourrait lui être calculée : elle n'est pas rejected, n'est pas déjà rapprochée, et son lot a atteint la banque.

Les résultats sortent par date d'opération croissante, sans pagination. day_offset compte les jours depuis la date de référence de l'instruction. Rien n'est enregistré : un résultat n'est pas une suggestion, n'apparaît pas dans la vue de règlement et ne sera pas suggéré plus tard. Pour le retenir, confirmez-le directement.

Un paramètre refusé répond un 422 validation_failed, avec details indexé par le nom du paramètre.

Confirmer le règlement d'une instruction​

Confirmer enregistre le paiement de la facture de l'instruction et la passe à settled. Rien ne part vers la banque, et l'annulation défait ce que la confirmation a enregistré.

Pour une suggestion, désignez-la par son id :

curl -X POST https://app.scribee.tech/api/v1/payment_batches/7301/settlement/suggestions/91204/confirm \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Idempotency-Key: 3c9e7a12-5f0b-4d6e-8a41-72b9c0d5e813"

Pour un résultat de la recherche manuelle, nommez la paire dans le corps :

curl -X POST https://app.scribee.tech/api/v1/payment_batches/7301/settlement/confirm \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Idempotency-Key: 9a41d2c7-0e3b-4f58-b6a2-5c18e7f094d3" \
-H "Content-Type: application/json" \
-d '{ "payment_instruction_id": 61005, "bank_operation_id": 30405, "expected_version": 6 }'

Les deux formes font la même chose :

  • La paire est contrôlée de nouveau au moment de la confirmation, avec les contrôles des suggestions, sur la date de l'opération elle-même : la fenêtre limite les suggestions, jamais une confirmation. L'opération ne doit régler aucune autre instruction, ni avoir été rapprochée d'une autre facture.
  • La facture est payée par le rapprochement bancaire. Si l'opération était déjà rapprochée de cette même facture, ce rapprochement et son paiement sont repris, jamais dupliqués. Une instruction qui ne règle aucune facture est rapprochée seule, sans paiement : son invoice_payment_id reste null.
  • Tant que ce lien existe, les affectations de l'opération ne changent que par l'annulation de ce règlement. L'opération ne peut être soldée contre aucune autre facture que celle de l'instruction - contre aucune, pour une instruction qui ne règle aucune facture -, et ses affectations ne peuvent être ni annulées ni redimensionnées : POST et DELETE sur /api/v1/bank_operations/{id}/reconciliation, et PATCH /api/v1/bank_allocations/{id}, répondent par un 422 operation_failed (Affectations et rapprochement).
  • L'instruction devient settled. Quand le lot est submitted et que chacune de ses instructions a un statut définitif, il passe à completed ou partially_completed, et payment_batch.updated est émis (Webhooks).
  • La suggestion confirmée, et toute autre suggestion de l'instruction ou de l'opération, disparaissent de la vue de règlement.

La réponse est un 200 portant la ligne de règlement de l'instruction, matched. Confirmer la paire déjà confirmée répond le même 200, sans rien écrire, quelle que soit la version envoyée.

Le corps peut porter expected_version, la version du lot telle que vous l'avez lue ; il est facultatif sur les deux formes. Un lot qui a changé depuis est refusé par un 409 stale_version, et rien n'est confirmé.

Annuler un règlement confirmé​

curl -X POST https://app.scribee.tech/api/v1/payment_batches/7301/settlement/instructions/61005/undo \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Idempotency-Key: d6b0f3a8-2c71-4e95-9f14-8e3a5b27c160"

Annuler ne rembourse rien et n'annule aucun virement à la banque. L'annulation retire le lien entre l'instruction et son opération, et seulement ce que la confirmation en avait tiré :

  • Le paiement de facture que la confirmation a enregistré est retiré, et lui seul : celui que l'instruction désigne par son invoice_payment_id. Une écriture comptable déjà exportée n'est jamais réécrite : une écriture de correction est passée. Un rapprochement de l'opération avec la facture qui existait avant la confirmation, et son paiement, restent en place, comme tout paiement que la confirmation n'a pas enregistré : l'annulation ne retire alors que le lien.
  • Les comptes rendus de la banque sont conservés.
  • Si la banque a signalé l'exécution de l'instruction, elle reste settled, et le lot garde son état. C'est vrai aussi quand ce signalement est arrivé après la confirmation : il ne change pas le status de l'instruction, déjà settled, mais il est conservé.
  • Sinon, l'instruction revient à pending - exécution à confirmer. Elle garde le montant de sa facture réservé, et elle n'est jamais remise une seconde fois à la banque. Un lot completed ou partially_completed revient à submitted, et payment_batch.updated est émis. La ligne de règlement porte alors execution_to_confirm_at, l'instant de l'annulation.

execution_to_confirm_at ne dit pas que le paiement a été annulé. L'absence d'opération bancaire ne prouve pas que le virement n'a pas été exécuté : son exécution n'est simplement plus démontrée. Le champ ne change pas le state de la ligne. Il revient à null quand la banque signale l'exécution de l'instruction, qui devient settled, quand elle la rejette, qui devient rejected, ou quand une opération bancaire est confirmée pour elle. Un nouveau statut pending communiqué par la banque le laisse en place. Sur un lot sepa_file, vous pouvez aussi déclarer l'instruction non exécutée.

La réponse est un 200 portant la ligne de règlement de l'instruction. Scribee recalcule ensuite les suggestions du lot en arrière-plan : l'opération peut être suggérée de nouveau, sous un nouvel id, sauf si vous l'aviez écartée. Une nouvelle confirmation repasse l'instruction à settled.

Annuler une instruction qui n'est pas rapprochée répond le même 200, sans rien écrire, quelle que soit la version envoyée. Le corps accepte le même expected_version facultatif que la confirmation.

Déclarer une instruction non exécutée​

curl -X POST https://app.scribee.tech/api/v1/payment_batches/6100/settlement/instructions/78012/not_executed \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Idempotency-Key: 5e2c8b71-a0d4-4f39-8c6e-1b7d93f24a05" \
-H "Content-Type: application/json" \
-d '{ "reason": "file_not_uploaded", "note": "The SEPA file was never uploaded to the bank portal.", "expected_version": 9 }'

Une instruction dont l'exécution est à confirmer - son execution_to_confirm_at est renseigné - garde le montant de sa facture réservé tant que rien ne tranche. Ce qui peut trancher dépend du canal du lot :

channel du lotCe qui résout une exécution à confirmer
hosted_consentCe que la banque signale - l'exécution ou le rejet de l'instruction -, ou la confirmation d'une opération bancaire. Une déclaration de non-exécution est refusée
sepa_fileLa confirmation d'une opération bancaire, ou votre déclaration de non-exécution : aucune banque ne répond à Scribee sur un fichier que vous avez transmis vous-même

Une opération bancaire absente ne prouve pas que le paiement n'a pas été exécuté. Ne trouver aucun débit sur le compte ne suffit pas : déclarez une non-exécution quand vous la constatez, et dites laquelle.

ChampCe qu'il porte
reasonExigé. Pourquoi le paiement n'a pas été exécuté : file_not_uploaded, le fichier n'a jamais été déposé à la banque ; rejected_by_bank_portal, le portail de la banque l'a refusé ; cancelled_at_bank, il a été annulé à la banque
noteExigé. Votre explication, conservée avec la déclaration : 2000 caractères au plus, une fois retirés les espaces qui l'entourent
expected_versionFacultatif. La version du lot telle que vous l'avez lue ; un lot qui a changé depuis est refusé par un 409 stale_version, et rien n'est déclaré

L'instruction doit appartenir au lot, et son exécution doit être à confirmer. Une instruction dont le règlement est confirmé ne se déclare pas : annulez d'abord ce règlement.

La déclaration :

  • passe l'instruction à rejected, définitivement, et remet son execution_to_confirm_at à null. La ligne de règlement porte not_executed_declaration, qui distingue votre déclaration d'un refus signalé par la banque ;
  • libère le montant réservé de la facture : un nouveau lot, créé dans Scribee, peut la régler ;
  • ne remet rien à la banque : l'instruction déclarée n'est jamais remise une seconde fois, et aucun nouveau lot n'est créé à votre place ;
  • conserve les comptes rendus de la banque ;
  • est enregistrée au nom de votre application, avec sa date ;
  • change la version du lot. Quand le lot est submitted et que chacune de ses instructions a désormais un statut définitif, il passe à rejected ou partially_completed, et payment_batch.updated est émis (Webhooks).

La réponse est un 200 portant la ligne de règlement de l'instruction :

{
"data": {
"payment_instruction_id": 78012,
"state": "unmatched",
"requires_review": false,
"bank_operation_id": null,
"invoice_payment_id": null,
"execution_to_confirm_at": null,
"not_executed_declaration": {
"reason": "file_not_uploaded",
"note": "The SEPA file was never uploaded to the bank portal.",
"declared_at": "2026-09-29T10:15:00+02:00"
},
"provider_evidence_conflict_at": null,
"late_provider_events": [],
"candidate_bank_operation_ids": [],
"matched_on": [],
"suggestions": []
}
}

Renvoyer la même déclaration - même reason, même note - sur une instruction qui la porte déjà répond le même 200, sans rien écrire, quelle que soit la version envoyée. Une déclaration différente est refusée : l'instruction n'est plus à confirmer.

Les refus​

StatutcodeQuand
422validation_failedL'Idempotency-Key manque sur une confirmation, une annulation ou une déclaration ; expected_version ne se lit pas comme un entier ; sur POST .../settlement/confirm, l'instruction n'appartient pas au lot ou l'opération n'appartient pas à l'entreprise du lot ; sur une déclaration, reason ou note est présent sans être une chaîne JSON, reason n'est pas l'une des trois valeurs, ou note est vide ou dépasse 2000 caractères. details nomme chaque champ refusé
422operation_failedLa paire est refusée, et message dit pourquoi : la suggestion n'est plus proposée, l'instruction est déjà rapprochée d'une autre opération ou ne peut pas être réglée, l'opération règle déjà une autre instruction, est rapprochée d'une autre facture ou ne passe pas les contrôles, ou le rapprochement bancaire refuse le paiement de la facture. Sur une annulation : le paiement enregistré ne peut pas être retiré. Sur une déclaration : le lot n'est pas un lot sepa_file, ou l'exécution de l'instruction n'est pas à confirmer. Sur toutes : le lot est en cours de mise à jour, réessayez dans un instant
409stale_versionexpected_version ne correspond plus à la version du lot. Rien n'a été confirmé, annulé ni déclaré ; details.current_version porte la version actuelle et details.payment_batch le lot
409idempotency_key_reuseLa même Idempotency-Key accompagne une requête différente
409idempotency_request_in_progressUn appel portant la même Idempotency-Key est encore en cours
404not_foundLe lot n'est pas atteignable par vos habilitations, ou la suggestion ou l'instruction du chemin n'appartient pas à ce lot

Embarquer les ressources liées​

La liste et la lecture des lots acceptent include, avec une ou deux valeurs séparées par une virgule :

includeClé ajoutéeContenu
payment_instructionspayment_instructionsToutes les instructions du lot, non paginées, dans l'ordre croissant de leur identifiant
bank_accountbank_accountLe compte débité, avec la charge utile complète de Comptes bancaires

Sans include, ces clés sont absentes - et non pas présentes à null. Toute autre valeur est ignorée. Les instructions n'acceptent aucun include.

Les montants​

total_amount et amount sont des nombres JSON arrondis à deux décimales, et non des chaînes : 1250.50 s'écrit 1250.5. total_amount est la somme des montants des instructions du lot.

Préparer, approuver et remettre un lot​

Sept opérations mènent un lot de sa création à sa remise. Elles appliquent les règles du centre de paiement de Scribee : un lot préparé par l'API porte les mêmes instructions, les mêmes identifiants et les mêmes refus qu'un lot préparé à l'écran.

Le parcours complet​

  1. POST /api/v1/workspaces/{workspace_id}/companies/{company_id}/payment_batches crée le lot, draft.
  2. PATCH /api/v1/payment_batches/{id} le corrige, tant qu'il est draft.
  3. POST /api/v1/payment_batches/{id}/submit_for_approval le passe à pending_approval.
  4. POST /api/v1/payment_batches/{id}/approve le passe à approved. POST /api/v1/payment_batches/{id}/refuse le renvoie à draft, où il se corrige avant d'être soumis de nouveau.
  5. POST /api/v1/payment_batches/{id}/submit remet un lot hosted_consent approuvé à son canal. Un lot sepa_file est remis par POST /api/v1/payment_batches/{id}/sepa_export (Générer et télécharger le fichier SEPA).
  6. POST /api/v1/payment_batches/{id}/consent_session publie, pour un lot hosted_consent, la page où le titulaire du compte donne son consentement (Reprendre le consentement).
  7. GET /api/v1/payment_batches/{id} suit le lot jusqu'à ce qu'il quitte approved, ou l'événement payment_batch.updated vous l'annonce (Être notifié d'un changement d'état).

Aucune de ces étapes ne prouve qu'un virement a été exécuté, pas même la remise : seuls l'état du lot et le statut de ses instructions en disent quelque chose (Les états d'un lot).

Version et idempotence​

OpérationIdempotency-Keyexpected_versionSuccès
POST /api/v1/workspaces/{workspace_id}/companies/{company_id}/payment_batchesexigéesans objet201
PATCH /api/v1/payment_batches/{id}acceptée, facultativefacultatif200
POST /api/v1/payment_batches/{id}/submit_for_approvalexigéefacultatif200
POST /api/v1/payment_batches/{id}/approveexigéeexigé200
POST /api/v1/payment_batches/{id}/refuseexigéefacultatif200
POST /api/v1/payment_batches/{id}/submitexigéeexigé202
POST /api/v1/payment_batches/{id}/consent_sessionignoréesans objet200

Les sept exigent le scope write, et aucun autre : l'approbation n'a pas de scope à part. Un token portant write, avec une habilitation sur le workspace du lot, peut approuver le lot qu'il a préparé. Scribee ne vérifie pas que l'approbation vient d'un autre client OAuth que la préparation ; si votre organisation sépare ces deux rôles, cette séparation est de votre ressort.

expected_version est la version du lot telle que vous l'avez lue, envoyée dans le corps de la requête. Sur les quatre transitions, le corps ne porte rien d'autre. Un lot qui a changé depuis votre lecture est refusé par un 409 stale_version, et rien n'est écrit ni envoyé ; details.current_version porte la version actuelle et details.payment_batch le lot tel que GET /api/v1/payment_batches/{id} le publie. Relisez-le, vérifiez que l'action a toujours lieu d'être, puis renvoyez l'appel avec details.current_version. Un expected_version exigé et absent, ou qui ne se lit pas comme un entier, est refusé par un 422 validation_failed, avec details.expected_version.

Avec la même Idempotency-Key, un rejeu renvoie la réponse enregistrée : un lot n'est pas créé deux fois, et une remise n'atteint jamais deux fois le canal. Sans elle, deux créations identiques créent deux lots. Seul un succès est enregistré : un refus ne consomme pas la clé, et l'appel corrigé peut être renvoyé avec la même clé.

Créer un brouillon​

curl -X POST https://app.scribee.tech/api/v1/workspaces/YOUR_WORKSPACE_ID/companies/YOUR_COMPANY_ID/payment_batches \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Idempotency-Key: 2b7e40a1-9c31-4d55-8f0b-6a1c2e93d770" \
-H "Content-Type: application/json" \
-d '{
"bank_account_id": 812,
"name": "Fournisseurs 2026-08",
"channel": "hosted_consent",
"execution_date": "2026-08-29",
"instructions": [
{
"invoice_document_id": 88120,
"amount": 830.0,
"beneficiary_name": "Papeterie Martin SARL",
"beneficiary_iban": "FR7630006000011234567890189",
"reference": "FA-2026-0310"
}
]
}'

La réponse est un 201 portant le lot, draft, sous la forme de sa lecture (Lire un lot).

  • bank_account_id, channel et instructions sont exigés ; name et execution_date sont facultatifs. channel vaut hosted_consent ou sepa_file.
  • bank_account_id désigne un compte bancaire de l'entreprise du chemin (Comptes bancaires). Le lot prend la devise de ce compte, ou EUR quand le compte n'en porte pas : le corps ne porte pas de devise de lot.
  • instructions porte au moins une ligne. Chaque ligne exige amount, beneficiary_name et beneficiary_iban. invoice_document_id désigne la facture de l'entreprise que la ligne règle, ou vaut null pour un paiement qui ne règle aucune facture Scribee. reference est la référence que le bénéficiaire voit.
  • Le currency_code d'une ligne est facultatif et vaut par défaut la devise du lot ; toute autre valeur est refusée. Une ligne qui règle une facture dans une autre devise est refusée aussi : Scribee ne convertit jamais un montant.
  • L'execution_date d'une ligne est facultative et vaut par défaut celle du lot.
  • beneficiary_iban est l'IBAN complet. Les espaces sont acceptés et retirés ; le format et la clé de contrôle sont vérifiés. Il n'est jamais renvoyé : ni cette réponse ni aucune autre ne publie plus que beneficiary_iban_masked et beneficiary_iban_last4 (Les instructions d'un lot).
  • Scribee attribue à chaque ligne son end_to_end_id.

Un brouillon ne réserve rien et ne déplace aucun argent. Le montant des factures réglées n'est réservé qu'au moment de la remise.

Un bank_account_id ou un invoice_document_id qui ne désigne pas un enregistrement de cette entreprise est refusé par un 422 invalid_argument, que l'enregistrement appartienne à quelqu'un d'autre ou n'existe pas : ce refus ne révèle rien des enregistrements qui ne sont pas les vôtres.

Modifier un brouillon​

curl -X PATCH https://app.scribee.tech/api/v1/payment_batches/7301 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "execution_date": "2026-09-01", "expected_version": 1 }'

La réponse est un 200 portant le lot modifié.

  • Seul un lot draft se modifie. Tout autre état est refusé par un 422 batch_not_submittable, quelle que soit la version envoyée. Pour corriger un lot pending_approval, refusez son approbation : il revient à draft.
  • Seuls les champs envoyés sont écrits. Un champ absent du corps garde sa valeur ; "name": null efface le libellé.
  • instructions, quand il est envoyé, remplace l'ensemble des lignes : une ligne absente de la liste est supprimée. Les nouvelles lignes reçoivent leurs end_to_end_id, numérotés de nouveau depuis le début.
  • Changer bank_account_id change la devise du lot. Les lignes conservées doivent être dans cette devise, sinon l'appel est refusé : envoyez alors instructions avec le nouveau compte.
  • Changer execution_date entraîne les lignes qui portaient l'ancienne date du lot ; une ligne qui portait sa propre date la garde.

Une modification ne change pas l'état du lot, et n'émet donc pas payment_batch.updated.

Soumettre à approbation, approuver, refuser​

curl -X POST https://app.scribee.tech/api/v1/payment_batches/7301/approve \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Idempotency-Key: 5d1f8c3e-7a24-4b90-9e61-0c3b7f2a8d45" \
-H "Content-Type: application/json" \
-d '{ "expected_version": 3 }'
OpérationÉtat de départÉtat d'arrivéeSur un lot déjà dans l'état d'arrivée
POST /api/v1/payment_batches/{id}/submit_for_approvaldraftpending_approvalRefusé : 422 batch_not_submittable
POST /api/v1/payment_batches/{id}/approvepending_approvalapproved200, rien ne change
POST /api/v1/payment_batches/{id}/refusepending_approvaldraft200, rien ne change

La réponse est un 200 portant le lot dans son nouvel état.

  • Soumettre à approbation exige au moins une instruction, toutes encore valides. Sinon, l'appel est refusé par un 422 validation_failed, et details.instructions nomme les end_to_end_id des lignes qui ne le sont plus.
  • Approuver renseigne approved_at. Le montant total du lot est alors figé.
  • Refuser renvoie le lot à draft, où il se modifie de nouveau.
  • Depuis tout autre état, la transition est refusée par un 422 batch_not_submittable.

Chaque transition émet payment_batch.updated ; un appel qui ne change rien n'émet rien (Webhooks).

Savoir qui a approuvé ou refusé un lot​

Chaque approbation et chaque refus enregistre qui l'a décidé : l'utilisateur, pour une décision prise dans le centre de paiement Scribee ; l'application API à laquelle le jeton a été délivré, pour un appel à POST /api/v1/payment_batches/{id}/approve ou POST /api/v1/payment_batches/{id}/refuse. Le lot le publie dans deux champs, présents dans toutes ses réponses :

ChampContenu
approved_byL'approbation du lot, ou null tant qu'il n'a pas été approuvé
refused_byLe refus d'approbation le plus récent, ou null si l'approbation du lot n'a jamais été refusée. Un lot refusé revient à draft et peut être soumis puis approuvé de nouveau : refused_by peut donc désigner un refus antérieur à approved_by

Chacun porte :

ChampContenu
actor_typeuser pour une décision prise dans le centre de paiement Scribee, api_client pour une décision prise par cette API
nameQui a décidé, tel qu'il se nommait à ce moment : pour user, le prénom et le nom de l'utilisateur, ou, s'il n'a renseigné ni l'un ni l'autre, un libellé fixe qui ne l'identifie pas, dans la langue par défaut de la plateforme (le français : « Utilisateur sans nom ») ; pour api_client, le nom de l'application API. Ce n'est jamais une adresse e-mail
decided_atLa date et l'heure de la décision. Sur approved_by, c'est la valeur de approved_at
  • Seule une décision qui change l'état du lot est enregistrée. Un appel sur un lot déjà dans l'état d'arrivée répond 200 sans rien enregistrer. De deux décisions simultanées, seule celle qui fait passer le lot est enregistrée.
  • Aucun auteur n'est déduit après coup. Une décision prise avant que Scribee n'enregistre son auteur n'en a pas : le lot publie alors approved_by à null bien que approved_at soit renseigné, et un tel refus n'apparaît pas dans refused_by.
  • Celui qui soumet un lot peut aussi l'approuver. Scribee n'exige pas que l'approbation vienne d'un autre utilisateur ou d'une autre application que la soumission.
curl -X POST https://app.scribee.tech/api/v1/payment_batches/7301/submit \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Idempotency-Key: 8e2a6f14-3c95-4d07-b1e8-9f4a2c6d0b73" \
-H "Content-Type: application/json" \
-d '{ "expected_version": 4 }'

submit remet à son canal un lot approved dont le channel vaut hosted_consent, et qui ne porte encore aucune tentative de remise. Un lot sepa_file est refusé par un 422 batch_not_submittable : il est remis par POST /api/v1/payment_batches/{id}/sepa_export.

Avant d'appeler le canal, Scribee réserve le montant de chaque facture réglée, comme pour un fichier SEPA. Une réservation refusée répond un 422 validation_failed, avec details indexé payment_instructions.<id> et pour valeur already_reserved, exceeds_remaining_due ou currency_mismatch ; le lot reste approved, sans tentative enregistrée, et rien n'est envoyé.

Scribee peut aussi refuser le lot avant tout envoi : quand le canal ne peut pas l'exprimer en l'état, ou quand la demande au prestataire ne peut pas être construite de notre côté. L'appel répond alors un 422 validation_failed, avec details.instructions ; rien n'est envoyé au prestataire, mais le lot passe à failed avec submission_refused_by à local, comme depuis le centre de paiement, ses instructions passent à submission_failed, et payment_batch.updated est émis. Ce refus ne consomme pas l'Idempotency-Key : un nouvel appel est traité de nouveau, mais un lot failed n'est jamais remis.

La réponse est un 202 dès que l'appel a atteint le canal, quelle qu'ait été sa réponse. Un 202 ne signifie jamais que l'argent a bougé. Lisez le lot qu'elle porte :

  • une demande de paiement en attente de consentement laisse le lot approved, avec submission_status à awaiting_consent : le titulaire du compte doit encore l'autoriser sur la page de sa banque ;
  • une issue inconnue laisse le lot approved, avec submission_status à unknown ;
  • un refus du prestataire passe le lot à failed, avec submission_refused_by à provider, error_code et error_message ;
  • un canal qui prend le lot le passe à submitted, ou plus loin si sa réponse porte déjà l'issue de chaque instruction.

Un lot resté approved se suit comme toute remise engagée (Une remise engagée dont l'issue n'est pas connue). La réponse ne porte jamais l'adresse de la page de consentement : POST /api/v1/payment_batches/{id}/consent_session la publie.

Renvoyer submit sur un lot que le canal a déjà pris répond le même 202, sans rien envoyer. Sur un lot dont une tentative est déjà engagée sans avoir été prise - consentement en attente, issue inconnue -, l'appel est refusé par un 422 batch_not_submittable : Scribee ne remet jamais deux fois les instructions d'un lot. Quand une autre requête est en train de remettre le même lot, l'appel est refusé de la même façon, et son message invite à relire le lot dans quelques instants. Quand aucun canal n'est disponible pour ce lot, l'appel est refusé par un 422 operation_failed. Aucun de ces refus n'a rien envoyé.

curl -X POST https://app.scribee.tech/api/v1/payment_batches/7301/consent_session \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
{
"data": {
"payment_batch_id": 7301,
"state": "pending",
"consent_url": "https://consent.example.com/payments/7Hq2/authorize",
"consent_url_expires_at": null
}
}

Un lot hosted_consent remis attend que le titulaire du compte autorise le paiement sur la page de sa banque. POST /api/v1/payment_batches/{id}/consent_session publie l'adresse de cette page. Le consentement se donne dans un navigateur, par le titulaire du compte : aucun appel de l'API ne le donne à sa place. Transmettez consent_url à cette personne, qui l'ouvre dans son navigateur.

  • Il n'y a de consentement qu'après submit. Un lot qui ne porte encore aucune tentative de remise, ou un lot sepa_file, est refusé par un 422 batch_not_submittable, sans que le prestataire de paiement soit interrogé.
  • Rien n'est créé, et rien n'est envoyé une seconde fois. La réponse est un 200, sans id ni horodatage : le consentement n'est pas une ressource enregistrée. Chaque appel interroge de nouveau le prestataire sur la demande de paiement que submit a créée ; un lot dont la remise est déjà résolue est répondu depuis son état, sans l'interroger.
  • Cette interrogation peut faire évoluer le lot. Un consentement que le prestataire a depuis accepté, laissé expirer ou refusé fait sortir le lot de approved, comme toute réponse du canal, et payment_batch.updated est émis.
  • Le scope write est exigé.
stateCe qu'il signifie
pendingLe titulaire du compte n'a pas encore consenti, ou la réponse du prestataire n'est pas connue. Le lot reste approved
completedLe prestataire a pris le paiement. Ce n'est pas la preuve que l'argent a bougé : suivez le lot et ses instructions
expiredLe prestataire indique que le consentement a expiré sans avoir été donné. Le lot est failed
failedLe prestataire a refusé le consentement pour une autre raison. Le lot est failed

consent_url n'est renseignée que lorsque state vaut pending et que le prestataire en a fourni une ; elle vaut null sinon. consent_url_expires_at vaut null sauf si le prestataire donne une échéance, et null ne signifie pas que l'adresse n'expire jamais. Le temps ne change rien : aucun state ne change, et aucun montant réservé n'est libéré, parce qu'un délai s'est écoulé.

consent_url est une capacité au porteur : quiconque la détient peut ouvrir la page de consentement. Ne la stockez pas, ne la journalisez pas, et redemandez-en une plutôt que de réutiliser une ancienne. La réponse porte Cache-Control: no-store, et l'endpoint ne prend pas d'Idempotency-Key : une clé envoyée est ignorée, et rien n'est enregistré ni rejoué.

Six appels par minute, par application OAuth et par lot. Au-delà, l'appel est refusé par un 429 rate_limited, avec l'en-tête Retry-After en secondes : attendez ce délai avant de réessayer. Rien n'est fait, et le prestataire n'est pas interrogé. Chaque appel qui atteint le lot compte, un refus 422 compris ; un 404 ne compte pas. Les autres lots, et les autres applications, sont comptés à part. Pour suivre l'issue du consentement, lisez GET /api/v1/payment_batches/{id} ou attendez payment_batch.updated, plutôt que d'appeler consent_session en boucle : demandez une adresse quand le titulaire du compte doit ouvrir la page.

Les refus des écritures​

StatutcodeQuand
422validation_failedL'Idempotency-Key manque là où elle est exigée ; expected_version manque là où il est exigé ou ne se lit pas comme un entier ; un champ du corps est refusé - instructions vide ou qui n'est pas une liste d'objets, une ligne dans une autre devise que le lot ou que sa facture, un IBAN mal formé, un channel inconnu, un bank_account_id absent, une execution_date qui n'est pas au format AAAA-MM-JJ ; sur submit_for_approval, un lot sans instruction ou dont une instruction n'est plus valide ; sur submit, une réservation refusée, ou un lot que Scribee refuse avant tout envoi, qui passe alors à failed avec submission_refused_by à local. details nomme le champ, et une ligne s'y écrit instructions[0] pour la première
422invalid_argumentbank_account_id ou un invoice_document_id ne désigne pas un enregistrement de l'entreprise du lot. Ce refus ne porte pas de details
422batch_not_submittableLe lot n'est pas dans l'état que l'opération exige : PATCH hors de draft, une transition depuis un autre état que son état de départ, submit sur un lot qui n'est pas approved, qui porte déjà une tentative, qu'une autre requête est en train de remettre, ou dont le channel est sepa_file ; consent_session sur un lot qui ne porte encore aucune tentative de remise, ou dont le channel est sepa_file. Ce refus ne porte pas de details ; message dit la cause
422operation_failedSur submit, aucun canal n'est disponible pour ce lot. Rien n'a été envoyé
409stale_versionexpected_version ne correspond plus à la version du lot. Rien n'a été écrit ni envoyé ; details.current_version porte la version actuelle et details.payment_batch le lot
409idempotency_key_reuseLa même Idempotency-Key accompagne une requête différente
409idempotency_request_in_progressUn appel portant la même Idempotency-Key est encore en cours
429rate_limitedSur consent_session, plus de six appels en une minute par la même application sur le même lot. Rien n'a été fait ; réessayez après le délai de l'en-tête Retry-After, en secondes

Générer et télécharger le fichier SEPA​

Un lot sepa_file quitte Scribee sous la forme d'un fichier de virements SEPA au format pain.001, que vous transmettez vous-même à votre banque. POST /api/v1/payment_batches/{id}/sepa_export le génère ; GET /api/v1/payment_batches/{id}/sepa_export le lit et le sert.

Un fichier généré, téléchargé ou remis à votre banque ne signifie pas que les virements ont été exécutés. Aucune de ces deux opérations ne rend compte de ce que la banque a fait, et aucune n'enregistre le paiement d'une facture. Le lot passe à submitted une fois la remise du fichier finalisée, et n'en sort que par la confirmation du règlement de ses instructions : Scribee ne reçoit pas de réponse de la banque sur un fichier SEPA.

Générer le fichier​

curl -X POST https://app.scribee.tech/api/v1/payment_batches/6100/sepa_export \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Idempotency-Key: f0a2c518-4b73-49de-9c21-3e8a5d70b1c9"

L'en-tête Idempotency-Key est exigé. Le lot doit être approved et son channel doit valoir sepa_file. La génération engage la remise du lot par les mêmes contrôles que le centre de paiement - approbation, version, réservation, tentative unique : une fois l'appel accepté, le lot porte un submitted_at et ses instructions ne peuvent plus être remises une seconde fois.

Le corps est facultatif. Il peut porter expected_version, la version du lot telle que vous l'avez lue :

{ "expected_version": 6 }

Sans expected_version, aucune version n'est contrôlée. Avec, un lot qui a changé depuis votre lecture est refusé par un 409 stale_version et aucun fichier n'est généré. Une valeur qui ne se lit pas comme un entier est refusée par un 422 validation_failed, avec details.expected_version.

La réponse est un 202 portant la ressource d'export, y compris pour un lot dont l'export a déjà été demandé : l'appel répond alors avec l'export existant, quelle que soit la version envoyée, et n'en génère jamais un second. La génération elle-même change la version du lot : une fois l'export créé, renvoyer l'appel avec la version lue avant la génération répond donc cet export, et non un 409. Rejouer la même Idempotency-Key renvoie la réponse enregistrée.

{
"data": {
"id": 4400,
"payment_batch_id": 6100,
"state": "generating",
"progress": 100,
"retryable": false,
"started_at": "2026-08-27T10:05:40+02:00",
"finished_at": "2026-08-27T10:05:40+02:00",
"format": "pain.001.001.09",
"filename": "sepa-20260829-SCB4F1A9C07E2B84D5A9E36C1F0B7D2A845.xml",
"byte_size": 4812,
"sha256": "9f2c4e71a0b3d58e6c17f2a94b0e3d8c5a61f7e20b94c3d18e5a7f06c2b94d31",
"generated_at": "2026-08-27T10:05:40+02:00",
"served_count": 0,
"first_served": null,
"last_served": null
}
}

progress, started_at et finished_at décrivent la production du fichier, et non sa remise : seul state dit si le fichier est téléchargeable. retryable vaut toujours false.

Attendre le fichier​

Interrogez GET /api/v1/payment_batches/{id}/sepa_export sans demander de XML : il répond la même ressource JSON. state vaut generating tant que la remise du fichier n'est pas finalisée, puis available - le lot est alors submitted. Ce sont ses deux seules valeurs. Le GET ne génère jamais de fichier : sur un lot dont l'export n'a jamais été demandé, il répond 404.

Télécharger le fichier​

curl https://app.scribee.tech/api/v1/payment_batches/6100/sepa_export \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Accept: application/xml" \
-o sepa.xml

Avec Accept: application/xml et un state à available, la réponse est le fichier lui-même, en pièce jointe nommée d'après filename. Avant cela, la même requête répond un 200 portant la ressource JSON : vérifiez le Content-Type de la réponse avant de l'enregistrer comme fichier. Seul un 200 porte du XML : une erreur répond toujours en JSON, même avec Accept: application/xml.

Une fois available, le fichier ne change plus : ses octets, son sha256 et son generated_at sont figés. Comparez sha256 à l'empreinte SHA-256 du fichier reçu - c'est ce qui prouve que le fichier transmis à votre banque est celui que Scribee a produit. Un fichier dont les octets ne correspondent plus à sha256 n'est jamais servi.

Savoir quand le fichier a été servi​

Chaque fois que les octets du fichier sont servis - téléchargés depuis le centre de paiement Scribee, ou par GET /api/v1/payment_batches/{id}/sepa_export avec Accept: application/xml -, Scribee l'enregistre. La ressource d'export le publie dans trois champs, présents dans toutes ses réponses, celle du POST comprise :

ChampContenu
served_countLe nombre de fois où le fichier a été servi, tous canaux confondus ; 0 s'il ne l'a jamais été
first_servedLe premier téléchargement, ou null si le fichier n'a jamais été servi
last_servedLe dernier téléchargement, ou null si le fichier n'a jamais été servi. C'est le même que first_served quand le fichier n'a été servi qu'une fois

first_served et last_served portent chacun :

ChampContenu
served_atLa date et l'heure où le fichier a été servi
channelweb pour un téléchargement depuis le centre de paiement Scribee, api pour un téléchargement par cette API
served_toÀ qui le fichier a été servi, tel qu'il se nommait à ce moment : sur web, le prénom et le nom de l'utilisateur, ou, s'il n'a renseigné ni l'un ni l'autre, un libellé fixe qui ne l'identifie pas, dans la langue par défaut de la plateforme (le français : « Utilisateur sans nom ») ; sur api, le nom de l'application API. Ce n'est jamais une adresse e-mail

Après un téléchargement depuis le centre de paiement puis un autre par l'API, la ressource se lit ainsi :

{
"data": {
"id": 4400,
"payment_batch_id": 6100,
"state": "available",
"progress": 100,
"retryable": false,
"started_at": "2026-08-27T10:05:40+02:00",
"finished_at": "2026-08-27T10:05:40+02:00",
"format": "pain.001.001.09",
"filename": "sepa-20260829-SCB4F1A9C07E2B84D5A9E36C1F0B7D2A845.xml",
"byte_size": 4812,
"sha256": "9f2c4e71a0b3d58e6c17f2a94b0e3d8c5a61f7e20b94c3d18e5a7f06c2b94d31",
"generated_at": "2026-08-27T10:05:40+02:00",
"served_count": 2,
"first_served": {
"served_at": "2026-08-27T11:02:13+02:00",
"channel": "web",
"served_to": "Claire Dupont"
},
"last_served": {
"served_at": "2026-08-28T09:14:50+02:00",
"channel": "api",
"served_to": "Compta Connect"
}
}
}

Servi signifie seulement que les octets du fichier ont été envoyés dans une réponse HTTP à cet utilisateur ou à cette application. Cela ne prouve ni que le fichier a été reçu, ni qu'il a été remis ou transmis à votre banque, ni que la banque a exécuté les virements. Seuls le state du lot et le status de ses instructions rendent compte de ce que la banque a fait.

Télécharger le fichier ne change rien d'autre : ni le lot, ni ses instructions, ni l'export, ni le paiement d'une facture. Ses octets, son sha256 et son generated_at restent ceux publiés. Répéter le téléchargement sert le même fichier et incrémente served_count à chaque fois. Ne sont pas comptés : la lecture de la ressource JSON, un 404, un refus operation_failed, ni un export encore generating. Si un téléchargement ne peut pas être enregistré, le fichier n'est pas servi : la réponse est une erreur, jamais le fichier.

Le scope exigé​

Les deux opérations exigent le scope write, y compris le GET : le fichier porte les IBAN complets des bénéficiaires, que la surface JSON ne publie jamais. Un token qui ne porte que read reçoit un 403.

Les refus​

StatutcodeQuand
422validation_failedLe POST n'a pas d'Idempotency-Key ; ou le lot échoue à la validation métier pain.001, et details.base porte le diagnostic enregistré : chaque règle en échec, suivie de ce qu'elle vise, batch ou l'end_to_end_id d'une instruction ; ou une instruction ne peut pas réserver le montant de sa facture, et details est indexé payment_instructions.<id>, avec pour valeur already_reserved, exceeds_remaining_due ou currency_mismatch
422validation_failedexpected_version ne se lit pas comme un entier ; details.expected_version le dit
422batch_not_submittableLe lot n'est pas approved, son channel est hosted_consent, sa tentative de remise s'est terminée sans fichier, ou une tentative de remise est toujours en cours sans avoir encore produit son export. Ce refus ne porte pas de details
422operation_failedSur le GET avec Accept: application/xml : le fichier conservé a disparu, ou ne correspond plus à son sha256
409stale_versionexpected_version ne correspond plus à la version du lot, et aucun export n'existe encore. Rien n'a été généré ; details.current_version porte la version actuelle et details.payment_batch le lot tel que GET /api/v1/payment_batches/{id} le publie
409idempotency_key_reuseLa même Idempotency-Key accompagne une requête différente
409idempotency_request_in_progressUn appel portant la même Idempotency-Key est encore en cours

Une tentative de remise toujours en cours se reconnaît à son message :

{
"error": "unprocessable_entity",
"code": "batch_not_submittable",
"message": "Une tentative de remise de ce lot de paiement est toujours en cours ; cet appel n'a rien généré. Relisez l'export dans quelques instants."
}

Renvoyez l'appel un peu plus tard : dès que cette tentative a produit son export, le POST répond un 202 avec lui.

Sur un 409 stale_version, relisez le lot depuis details, vérifiez qu'il doit toujours être remis, puis renvoyez l'appel avec details.current_version comme expected_version.

Un 404 dit que le lot n'est pas atteignable par vos habilitations ou, sur le GET, qu'aucun export n'a jamais été demandé pour ce lot.

Un refus 422 ne consomme pas la Idempotency-Key. Il ne rend pas pour autant chaque lot à nouveau générable. Un refus de réservation laisse le lot approved, sans tentative enregistrée : corrigez la cause et renvoyez l'appel. Un échec de la validation pain.001 est définitif : le lot passe à failed, avec submission_refused_by à local, et un nouvel appel répond batch_not_submittable.

Les erreurs​

StatuterrorQuand
400bad_requestUn page qui n'est pas un entier supérieur ou égal à 1, ou une page au-delà de la dernière page d'une collection non vide
403forbiddenLe token ne porte pas le scope exigé - read, ou write sur les écritures des lots, sur sepa_export et sur les POST de settlement -, votre client OAuth n'a pas d'habilitation sur ce workspace, ou le rapprochement bancaire y est désactivé
404not_foundL'entreprise, le lot, l'instruction ou la suggestion n'est pas atteignable par vos habilitations

Une page au-delà de la fin :

{
"error": "bad_request",
"message": "Le numéro de page dépasse le nombre de pages disponibles"
}

Un page qui n'est pas un entier valide porte le message "Le numéro de page doit être un entier supérieur ou égal à 1". Sur une collection vide, aucun numéro de page ne déborde : la réponse est un 200 avec une collection vide.

Sur les chemins par identifiant, l'atteignabilité est tranchée avant la fonctionnalité. Un lot ou une instruction que vos habilitations ne couvrent pas répond 404, jamais 403, même si le rapprochement bancaire est désactivé dans son workspace. Un 403 sur ces chemins ne confirme donc jamais qu'un identifiant existe hors de vos habilitations.

{
"error": "not_found",
"message": "La ressource demandée est introuvable"
}

Référence API​