optii by chut.app

Un endpoint.
Un en-tête.
Un fichier.

Tout ce qui suit est vérifié contre l’API en service. URL de base https://api.optii.eu

Authentification

Envoyez votre clé dans l’en-tête X-API-Key. Les clés commencent par optii_sk_ et ne sont affichées qu’une fois, à la création. Les clés sont stockées hachées ; une clé perdue se remplace, elle ne se récupère pas.

Chaque réponse porte un en-tête X-Request-ID. Citez-le quand vous nous écrivez.

curl https://api.optii.eu/ocr/extract \
  -H "X-API-Key: optii_sk_…" \
  -F "file=@document.pdf"

POST/ocr/extract

Envoi multipart d’un fichier. Renvoie chaque ligne avec sa boîte englobante et sa confiance, dans l’ordre de lecture, page après page.

Paramètres de requête

structured
false (défaut) pour les lignes, true pour les champs de documents belges (section suivante).
format
v2 (défaut). legacy renvoie le format de facture à plat des clients de 2025 ; ne l’utilisez pas pour du nouveau code.

Réponse 200

{
  "success": true,
  "text": "DUMONT & FILS SPRL\nRue de la Loi 42 · 1000 Bruxelles\nTVA BE0202239951\n…",
  "lines": [
    { "text": "DUMONT & FILS SPRL", "confidence": 0.996, "bbox": [[50.0, 50.0], [304.0, 50.0], [304.0, 84.0], [50.0, 84.0]] },
    { "text": "Rue de la Loi 42 · 1000 Bruxelles", "confidence": 0.991, "bbox": [[56.0, 82.0], [286.0, 82.0], [286.0, 100.0], [56.0, 100.0]] },
    { "text": "TVA BE0202239951", "confidence": 0.993, "bbox": [[56.0, 106.0], [198.0, 106.0], [198.0, 124.0], [56.0, 124.0]] }
  ],
  "confidence": 0.991,
  "source": "ocr",
  "quality": { "blur": 812.4, "long_side_px": 2339, "low_confidence_ratio": 0.0, "warnings": [] },
  "file_hash": "a3b2c1d4e5f6",
  "processing_time_ms": 342
}

Champs

text
le texte complet, lignes jointes par des sauts de ligne
lines[]
un objet par ligne : text, confidence (0 à 1), bbox sous forme de quatre coins [[x, y], …] en pixels de page, dans le sens horaire depuis le coin supérieur gauche
confidence
confiance moyenne des lignes
source
text, ocr ou hybrid, voir Source et qualité
quality
blur, long_side_px, low_confidence_ratio, warnings[]
file_hash
SHA-256 du fichier, premiers caractères ; le même fichier donne toujours la même valeur
processing_time_ms
temps serveur en millisecondes, envoi exclu

POST/ocr/extract?structured=true

Le même appel avec ?structured=true. Le moteur détecte le type de document d’après son texte (invoice, credit_note, debit_note, receipt, sinon unknown), extrait les champs belges et lance les contrôles. Quand le classifieur est confiant, document_type porte son label à la place.

Un champ absent de la page vaut null. Les montants sont des nombres avec un point, les dates sont gardées telles qu’imprimées (jj/mm/aaaa sur la plupart des documents belges).

{
  "document_type": "invoice",
  "source": "ocr",
  "quality": { "blur": 812.4, "long_side_px": 2339, "low_confidence_ratio": 0.0, "warnings": [] },
  "supplier_name": "DUMONT & FILS SPRL",
  "supplier_vat": "BE0202239951",
  "supplier_iban": "BE68539007547034",
  "document_number": "2024-0847",
  "document_date": "15/03/2024",
  "due_date": "14/04/2024",
  "structured_communication": "+++123/4567/89002+++",
  "currency": "EUR",
  "line_items": [
    { "description": "Conseil juridique — mars 2024", "quantity": 10, "unit_price": 125.0, "line_total": 1250.0, "vat_rate": 21.0 }
  ],
  "vat_subtotals": [ { "rate": 21.0, "base_amount": 1250.0, "vat_amount": 262.5 } ],
  "total_excl_vat": 1250.0,
  "total_vat": 262.5,
  "total_incl_vat": 1512.5,
  "validations": [
    { "check": "vat_checkdigit", "passed": true, "message": "" },
    { "check": "iban_mod97", "passed": true, "message": "" },
    { "check": "structured_comm_mod97", "passed": true, "message": "" },
    { "check": "totals_sum", "passed": true, "message": "" }
  ],
  "confidence": 0.91,
  "processing_time_ms": 342
}

Contrôles

vat_checkdigit
les chiffres de contrôle du numéro de TVA belge (97 moins les huit premiers chiffres mod 97)
iban_mod97
l’IBAN réarrangé mod 97 vaut 1
structured_comm_mod97
les chiffres de contrôle de la communication structurée +++xxx/xxxx/xxxxx+++
totals_sum · vat_subtotals_sum · vat_bases_sum
total_excl_vat + total_vat = total_incl_vat au cent près
line_items_sum
la somme des lignes égale le total HTVA quand elles sont présentes

POST/ocr/classify

Envoi multipart d’un fichier. Le texte est lu (couche texte ou OCR) et classé par un modèle XLM-RoBERTa parmi 32 types de documents belges.

Paramètres de requête

top_k
nombre de prédictions renvoyées, de 1 à 20, 5 par défaut.

Réponse 200

{
  "success": true,
  "document_type": "acc_purchase_invoice",
  "confidence": 0.9361,
  "confident": true,
  "predictions": [
    { "label": "acc_purchase_invoice", "confidence": 0.9361, "rank": 1 },
    { "label": "acc_sales_invoice", "confidence": 0.0298, "rank": 2 },
    { "label": "acc_credit_note", "confidence": 0.0112, "rank": 3 }
  ],
  "processing_time_ms": 388
}
document_type
le label quand confident vaut true, sinon unknown
confident
true quand la probabilité la plus haute atteint le seuil et que le label n’est pas un repli
predictions[]
label, confidence, rank, meilleur en premier

Les labels sont en snake_case avec un préfixe de famille, par exemple acc_purchase_invoice ou acc_sales_invoice. La liste complète est dans le tableau de bord.

Source et qualité

Chaque réponse d’extraction indique d’où vient le texte.

text
le PDF avait une couche texte utilisable ; les caractères sont exacts, chaque ligne a une confiance de 1
ocr
chaque page est passée par le moteur
hybrid
certaines pages avaient une couche, d’autres non

quality quality est calculé sur les pages passées par l’OCR :

blur
variance du laplacien sur une image en niveaux de gris réduite à 1500 px ; plus bas, plus flou
long_side_px
le plus petit grand côté de page en pixels
low_confidence_ratio
part des lignes sous 0.80 de confiance
warnings[]
l’un de low_resolution (sous 1500 px), blurry (sous 400), low_confidence (ratio au-dessus de 0.15)

Quand warnings n’est pas vide, montrez la page à l’utilisateur et demandez un nouveau scan. Les chiffres peuvent être justes ; vous ne pouvez pas le savoir.

Idempotence

Envoyez un en-tête Idempotency-Key (toute chaîne jusqu’à 255 caractères, un UUID convient). Pendant 24 heures, la même clé avec le même fichier et les mêmes paramètres renvoie la réponse stockée, sans seconde lecture ni second décompte. La même clé avec un autre fichier répond 409 idempotency_conflict.

L’empreinte est le SHA-256 du fichier plus les paramètres ; un envoi multipart rejoué correspond donc même si la boundary diffère.

Limites de débit et quotas

Les limites sont par plan et par clé. Le tableau est la configuration en service.

PlanRequêtes / minFichier maxClésQuota mensuel
Free105 MB1500
Pro6020 MB510 000
Enterprise30020 MB100illimité

Au-delà de la limite de débit : 429 rate_limited avec Retry-After. Au-delà du quota : 429 avec un message qui nomme le quota. L’endpoint public de démo autorise 10 requêtes par minute et par IP et 5 MB.

Erreurs

Chaque erreur est un JSON à deux champs : error, un code en snake_case, et message, une phrase pour les humains. Ne parsez jamais le message.

HTTP/1.1 413 Payload Too Large
X-Request-ID: 6f1c0a2b9d3e
Content-Type: application/json

{ "error": "file_too_large", "message": "File too large (max 5MB)" }
StatuterrorQuandQue faire
400unsupported_file_typeextension hors pdf png jpg jpeg tiff tif bmp webpconvertissez le fichier
401unauthorizedclé absente ou inconnuevérifiez la clé dans le tableau de bord
403forbiddenle plan ne le permet pas (nombre de clés, taille)voir les tarifs
409idempotency_conflictmême Idempotency-Key, fichier différentutilisez une nouvelle clé
413file_too_largeau-delà du fichier max du plancompressez ou découpez
422validation_failedpas de champ file, mauvais paramètre de requêterelisez la section requête
429rate_limitedlimite de débit ou quota mensuelattendez Retry-After, ou passez au plan supérieur
500processing_failedle moteur n’a pas pu lire le fichierréessayez une fois, puis écrivez-nous avec l’id de requête
503model_unavailablele moteur se charge après un redémarrageréessayez dans quelques secondes
503db_unavailablebase d’usage indisponible ; les lectures fonctionnentréessayez

Formats et limites

Entrée : PDF (texte ou scanné, multipage), PNG, JPG, TIFF, BMP, WebP. Max 20 MB avec une clé, 5 MB sur la démo. Langues lues : français, néerlandais, allemand, anglais ; le moteur gère 50 écritures mais l’extraction de champs et le classifieur sont belges.

Sortie : JSON, UTF-8. L’ordre des lignes est l’ordre de lecture par page. Les boîtes sont des polygones dans l’espace de pixels de la page rendue.

Extraits de code

Aucun SDK à installer. Trois extraits qui fonctionnent tels quels.

curl -X POST "https://api.optii.eu/ocr/extract?structured=true" \
  -H "X-API-Key: optii_sk_…" \
  -H "Idempotency-Key: $(uuidgen)" \
  -F "file=@facture-2024-0847.pdf"

OpenAPI

Le contrat lisible par machine est à https://api.optii.eu/openapi.json. C’est le manifeste d’outils sélectionné qu’utilise la plateforme chut.app ; les routes du tableau de bord n’y figurent pas.

Changelog

Voir le changelog →