Révision des champs
Confirmer ou corriger chaque champ extrait, et confier une facture à un réviseur externe via un lien hébergé à durée limitée.
La révision des champs enregistre une décision par champ extrait — confirmé, corrigé ou écarté — à côté de la valeur produite à l'origine par l'extraction. Rien n'est écrasé : la correction est stockée à côté de l'original, et la facture extraite elle-même reste inchangée.
Deux voies d'accès existent. Les révisions de champs sur une facture font partie de l'API publique et fonctionnent avec toute clé API active. Les sessions de révision hébergées confient une facture unique à quelqu'un qui n'a pas de compte Parsurai, via un lien à jeton, et sont désactivées par défaut.
Endpoints
| Méthode | Chemin | Authentification | Indicateur de fonctionnalité |
|---|---|---|---|
GET | /v1/invoices/{invoice_id}/field-reviews | Clé API | — |
PATCH | /v1/invoices/{invoice_id}/field-reviews | Clé API | — |
POST | /v1/invoices/{invoice_id}/review-sessions | Clé API | Requis |
GET | /v1/review-sessions/{token} | Jeton de session | Requis |
GET | /v1/review-sessions/{token}/field-reviews | Jeton de session | Requis |
PATCH | /v1/review-sessions/{token}/field-reviews | Jeton de session | Requis |
Les deux routes /v1/invoices/{invoice_id}/field-reviews ne sont soumises ni à l'offre ni à un indicateur de fonctionnalité. Tout ce qui figure sous Sessions de révision hébergées l'est.
Lire les révisions de champs
curl "https://api.parsurai.com/v1/invoices/2f7d7f8a-32fb-4a7c-a3b1-53b61b64252f/field-reviews" \
-H "X-API-Key: $PARSURAI_API_KEY"
{
"invoice_id": "2f7d7f8a-32fb-4a7c-a3b1-53b61b64252f",
"reviews": [
{
"field_path": "vendor.iban",
"original_value": "DE44500105175407324931",
"corrected_value": "DE02120300000000202051",
"review_state": "edited",
"confidence": null,
"reason_codes": ["low_confidence", "manual_edit"],
"reviewed_by_user_id": "8b2a1e4c-5f31-4d0b-9a77-6c3e2d1f4a90",
"reviewed_at": "2026-06-14T09:12:44Z",
"version": 1
}
],
"summary": {
"needs_review": 7,
"edited": 1,
"confirmed": 0,
"dismissed": 0,
"review_completion": 0.125
}
}
| Champ | Type | Description |
|---|---|---|
field_path | string | Le champ révisé, en notation pointée |
original_value | any | La valeur lue sur le document, résolue au moment de l'écriture |
corrected_value | any | La valeur fournie par le réviseur, ou null |
review_state | string | needs_review, confirmed, edited ou dismissed |
confidence | number | null | Réservé. Les révisions écrites via cette API contiennent toujours null |
reason_codes | string[] | Pourquoi le champ a été signalé ou modifié |
reviewed_by_user_id | UUID | null | Le compte auquel la décision a été rattachée |
reviewed_at | timestamp | null | Quand la décision a été enregistrée |
version | integer | Compteur de concurrence optimiste, incrémenté à chaque écriture |
reviews ne contient que les champs réellement révisés. Une facture que personne n'a touchée renvoie un tableau vide.
Synthèse
summary est calculée sur huit champs essentiels fixes — vendor.name, vendor.iban, buyer.name, invoice_number, issue_date, due_date, total_amount.amount, currency — plus tout autre champ déjà révisé. Un champ essentiel sans révision compte comme needs_review.
| Champ | Signification |
|---|---|
needs_review | Champs en attente d'une décision |
edited | Champs dont la valeur a été corrigée |
confirmed | Champs acceptés tels qu'extraits |
dismissed | Champs sur lesquels le réviseur a choisi de ne pas agir |
review_completion | (confirmed + edited + dismissed) / total, de 0.0 à 1.0 |
Une facture sans aucune révision rapporte donc needs_review: 8 et review_completion: 0.0.
Soumettre des décisions
PATCH /v1/invoices/{invoice_id}/field-reviews applique un lot de décisions en une seule transaction. Soit toutes les mises à jour du corps de requête aboutissent, soit aucune.
curl -X PATCH "https://api.parsurai.com/v1/invoices/2f7d7f8a-32fb-4a7c-a3b1-53b61b64252f/field-reviews" \
-H "X-API-Key: $PARSURAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"updates": [
{
"field_path": "vendor.iban",
"review_state": "edited",
"corrected_value": "DE02120300000000202051",
"reason_codes": ["manual_edit"],
"expected_version": 0
},
{
"field_path": "invoice_number",
"review_state": "confirmed",
"expected_version": 1
}
]
}'
| Champ | Type | Requis | Description |
|---|---|---|---|
field_path | string | Oui | Doit être un chemin révisable (voir ci-dessous) |
review_state | string | Oui | needs_review, confirmed, edited ou dismissed |
corrected_value | any | Non | null par défaut |
reason_codes | string[] | Non | Liste vide par défaut |
expected_version | integer | Oui | La version lue en dernier, 0 ou plus |
La réponse est le même lot que celui renvoyé par le GET, reflétant toutes les révisions de la facture après l'écriture.
Concurrence optimiste
Un champ jamais révisé n'a pas d'enregistrement, et cette absence vaut version 0. Envoyez expected_version: 0 pour la première décision sur un champ ; l'enregistrement créé porte version: 1. Chaque écriture suivante doit envoyer la version lue en dernier, et l'incrémente de un.
Si la version stockée diffère, le lot entier est rejeté avec 409 field_review_conflict. L'erreur contient l'état serveur actuel du champ en conflit, ce qui vous permet de montrer au réviseur ce qui a changé entre-temps et de réessayer avec la version à jour :
{
"code": "field_review_conflict",
"message": "Field review for path 'vendor.iban' has version conflict. DB: 2, expected: 1.",
"current_review": {
"field_path": "vendor.iban",
"review_state": "confirmed",
"version": 2
}
}
Codes de motif
| Code | Signification |
|---|---|
low_confidence | L'extraction n'était pas sûre de la valeur |
validator_warning | Une règle de validation a signalé la valeur |
missing_value | Le champ était absent du document |
manual_edit | Une personne a modifié la valeur |
source_mismatch | La valeur contredit une autre source |
stale_update | La valeur a été écrite à partir de données périmées |
Champs révisables
Seuls les champs scalaires de la facture peuvent être révisés. Tout autre chemin renvoie 422 invalid_field_review.
- Champs de partie sous
vendor.etbuyer.:name,address,email,phone,tax_id,iban,bank_name invoice_number,currency,payment_instructionsissue_date,due_date,billing_period_start,billing_period_endsubtotal.amount,discount_total.amount,tax_total.amount,total_amount.amountreference_numbers[n].valueline_items[n].:description,quantity,unit,unit_price,discount,total,tax_ratetax_breakdown[n].:rate,taxable_amount,tax_amount
Un chemin qui aboutit à un objet ou à un tableau est également refusé — seules les valeurs terminales sont révisables.
Gestion des erreurs
| Statut | Code | Cause typique |
|---|---|---|
404 | — | La facture n'est pas dans votre workspace |
409 | field_review_conflict | expected_version ne correspond pas à la version stockée |
422 | invalid_field_review | Le chemin n'est pas révisable, ou n'aboutit pas à une valeur scalaire |
404 traite de façon identique une facture inexistante et une facture appartenant à un autre workspace : l'API ne permet donc pas de confirmer l'existence de l'ID de facture de quelqu'un d'autre.
Sessions de révision hébergées
Les sessions de révision hébergées s'activent par déploiement et sont désactivées par défaut.
Le verrou est côté serveur et se ferme par défaut : hosted_review_sessions_enabled vaut false, donc une variable d'environnement non définie signifie désactivé. Il n'existe aucun contournement par en-tête, paramètre de requête ou route interne. Tant que la fonctionnalité est éteinte, chaque route de cette section répond 404 avec le code hosted_review_disabled — de façon identique, que le jeton de l'URL ait existé ou non.
Vérifiez auprès de l'exploitant de votre déploiement avant de construire dessus, et considérez la fonctionnalité comme éteinte tant qu'elle n'a pas été activée délibérément.
Une session de révision confie une facture à un réviseur qui n'a pas de compte Parsurai. Le jeton de session est la seule authentification : il s'authentifie seul, sans connexion derrière lui.
Créer une session
POST /v1/invoices/{invoice_id}/review-sessions attend l'origine autorisée à intégrer la page de révision.
curl -X POST "https://api.parsurai.com/v1/invoices/2f7d7f8a-32fb-4a7c-a3b1-53b61b64252f/review-sessions" \
-H "X-API-Key: $PARSURAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"allowed_origin": "https://ap.example.com"}'
allowed_origin est une origine sérialisée — schéma, hôte et port optionnel, sans chemin ni barre oblique finale — ou * pour désactiver la vérification.
{
"token": "0m3Zx1qKQ8w7nS2vB4tR9pL6dY5hJ0cA1fG3eU8iO2s",
"expires_at": "2026-06-14T10:12:44Z",
"allowed_origin": "https://ap.example.com"
}
Le jeton n'est renvoyé qu'une seule fois et n'est stocké que sous forme de hachage : il ne peut donc pas être relu ensuite. Il est valable une heure à compter de sa création.
Utiliser une session
Le réviseur ouvre https://www.parsurai.com/{locale}/review/{token}, qui affiche la page de révision hébergée par Parsurai pour cette session. Votre propre interface peut appeler directement les routes de session à la place :
| Méthode | Chemin | Renvoie |
|---|---|---|
GET | /v1/review-sessions/{token} | invoice_id, allowed_origin, expires_at, invoice_json |
GET | /v1/review-sessions/{token}/field-reviews | Le même lot que la route de facture |
PATCH | /v1/review-sessions/{token}/field-reviews | Le même lot, après l'écriture |
invoice_json est le résultat d'extraction brut de la facture derrière le jeton.
Le jeton circule dans le chemin de l'URL : ces réponses sont donc envoyées avec Cache-Control: no-store, private et Referrer-Policy: no-referrer. Traitez le lien lui-même comme un secret — tout ce qui journalise, met en cache ou transmet l'URL le divulgue.
Quand un lien cesse de fonctionner
| Statut | Cause |
|---|---|
401 | Le jeton est inconnu, ou la session a expiré — une session expirée est supprimée immédiatement |
401 | Le compte pour lequel le lien a été émis n'existe plus |
428 | Ce compte doit réaccepter les documents juridiques ; le lien est bloqué exactement comme le serait un utilisateur connecté |
404 | Les sessions de révision hébergées ne sont pas activées sur ce déploiement |
Événements de webhook
La révision des champs déclenche deux des événements listés dans l'aperçu des webhooks :
review.requiredà la création d'une session de révision — il ne se déclenche donc que là où les sessions hébergées sont activées. Sa payload contientinvoice_id,review_session_idetfields_flagged, le nombre de champs dont la confiance d'extraction était inférieure à0.85.review.completeddès qu'un lot de décisions ramènesummary.needs_reviewà zéro. Sa payload contientinvoice_idetreview_session_id. Celui-ci se déclenche aussi pour les décisions envoyées via les routes de facture ; un lot sans session reçoit unreview_session_idgénéré.
Relisez le lot courant avant chaque écriture. C'est expected_version qui empêche deux réviseurs de s'écraser mutuellement sans le savoir, et il n'est utile que si la version envoyée est celle que vous venez de lire.