Documentation

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éthodeCheminAuthentificationIndicateur de fonctionnalité
GET/v1/invoices/{invoice_id}/field-reviewsClé API—
PATCH/v1/invoices/{invoice_id}/field-reviewsClé API—
POST/v1/invoices/{invoice_id}/review-sessionsClé APIRequis
GET/v1/review-sessions/{token}Jeton de sessionRequis
GET/v1/review-sessions/{token}/field-reviewsJeton de sessionRequis
PATCH/v1/review-sessions/{token}/field-reviewsJeton de sessionRequis

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
  }
}
ChampTypeDescription
field_pathstringLe champ révisé, en notation pointée
original_valueanyLa valeur lue sur le document, résolue au moment de l'écriture
corrected_valueanyLa valeur fournie par le réviseur, ou null
review_statestringneeds_review, confirmed, edited ou dismissed
confidencenumber | nullRéservé. Les révisions écrites via cette API contiennent toujours null
reason_codesstring[]Pourquoi le champ a été signalé ou modifié
reviewed_by_user_idUUID | nullLe compte auquel la décision a été rattachée
reviewed_attimestamp | nullQuand la décision a été enregistrée
versionintegerCompteur 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.

ChampSignification
needs_reviewChamps en attente d'une décision
editedChamps dont la valeur a été corrigée
confirmedChamps acceptés tels qu'extraits
dismissedChamps 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
      }
    ]
  }'
ChampTypeRequisDescription
field_pathstringOuiDoit être un chemin révisable (voir ci-dessous)
review_statestringOuineeds_review, confirmed, edited ou dismissed
corrected_valueanyNonnull par défaut
reason_codesstring[]NonListe vide par défaut
expected_versionintegerOuiLa 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

CodeSignification
low_confidenceL'extraction n'était pas sûre de la valeur
validator_warningUne règle de validation a signalé la valeur
missing_valueLe champ était absent du document
manual_editUne personne a modifié la valeur
source_mismatchLa valeur contredit une autre source
stale_updateLa 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. et buyer. : name, address, email, phone, tax_id, iban, bank_name
  • invoice_number, currency, payment_instructions
  • issue_date, due_date, billing_period_start, billing_period_end
  • subtotal.amount, discount_total.amount, tax_total.amount, total_amount.amount
  • reference_numbers[n].value
  • line_items[n]. : description, quantity, unit, unit_price, discount, total, tax_rate
  • tax_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

StatutCodeCause typique
404—La facture n'est pas dans votre workspace
409field_review_conflictexpected_version ne correspond pas à la version stockée
422invalid_field_reviewLe 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éthodeCheminRenvoie
GET/v1/review-sessions/{token}invoice_id, allowed_origin, expires_at, invoice_json
GET/v1/review-sessions/{token}/field-reviewsLe même lot que la route de facture
PATCH/v1/review-sessions/{token}/field-reviewsLe 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

StatutCause
401Le jeton est inconnu, ou la session a expiré — une session expirée est supprimée immédiatement
401Le compte pour lequel le lien a été émis n'existe plus
428Ce compte doit réaccepter les documents juridiques ; le lien est bloqué exactement comme le serait un utilisateur connecté
404Les 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 contient invoice_id, review_session_id et fields_flagged, le nombre de champs dont la confiance d'extraction était inférieure à 0.85.
  • review.completed dès qu'un lot de décisions ramène summary.needs_review à zéro. Sa payload contient invoice_id et review_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 un review_session_id gé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.