Last update: October 15, 2026
Doc Extract API¶
Local port (Docker Compose): 30002 | Repo: solyntek/djinn/services/djinn-doc-extract
Extraction of structured data from invoices, receipts, resumes and ID documents via OCR + LLM.
Endpoints¶
| Method | Route | Description |
|---|---|---|
GET |
/health |
Health check |
GET |
/v1/schemas |
Schemas of supported document types |
POST |
/v1/extract |
Extraction from a single file |
POST |
/v1/extract/batch |
Batch extraction from multiple files |
POST |
/v1/extract/receipts/analyze |
Receipt batch control (max 100 files): duplicates, policy gaps, consistency, authenticity |
GET / PUT |
/v1/receipt-policies |
Read / replace the expense policy of your tenant |
All routes except /health require X-API-Key. Files (multipart/form-data, field file or files): .pdf, .docx, .png, .jpg, .jpeg, .tiff, .bmp, .gif, up to 50 MiB each. document_type is optional: when omitted, the type is detected from the text.
Gateway routing
The Kong gateway currently routes /v1/extract/* and /v1/schemas. /v1/receipt-policies has no public gateway route yet: it is documented here because the service exposes it.
Document types¶
| Type | Extracted data |
|---|---|
invoice |
vendor, number, date, line items, totals, VAT, IBAN |
receipt |
vendor, date, line items, totals, payment method |
resume |
name, email, skills, work experience, degrees, certifications |
id_card |
name, date of birth, nationality, document number |
rib |
holder, IBAN, BIC, bank, agency code, account number, RIB key |
Receipt control (/v1/extract/receipts/analyze)¶
Send up to 100 receipts (files, optional categories: a JSON array aligned by position with the files). For each receipt the response gives the extracted data, findings (internal consistency gaps such as total_mismatch or future_date, and expense-policy gaps such as amount_above_cap), and authenticity (the Doc Trust opinion, or null when it could not be obtained: "not evaluated", never "no signal"). duplicate_groups lists the receipts judged to be the same purchase (rule: fingerprint or vendor_date_amount). These are evaluations for a human review, never authorisations: no receipt is rejected. An unreadable or failing file produces an item with error and never stops the batch. The policy (currency, default_max_amount, caps per category, require_vat_above) is set with PUT /v1/receipt-policies; without one, a default policy applies (is_default: true).
Errors¶
Errors use FastAPI's format, {"detail": "..."}:
| Status | Cause |
|---|---|
400 |
File extension not allowed |
401 |
Missing or invalid API key |
413 |
File larger than 50 MiB; more than 100 receipts in a receipt batch |
422 |
Unreadable PDF (corrupt, not a PDF, password-protected), empty receipt batch, invalid categories or policy |
On /v1/extract/batch, an oversized or failing file does not fail the call: the item comes back with confidence: 0 and an error message.
Example¶
{
"document_type": "resume",
"data": {
"firstname": "Jean",
"lastname": "Dupont",
"skills": ["Python", "Go", "Docker"],
"experiences": [{"company": "Acme", "title": "Senior Dev"}]
},
"confidence": 0.87
}
Interactive reference¶
Generated on every build from the versioned spec openapi/doc-extract-v1.json (ADR-011) — no route is documented by hand.
Djinn Document Extract 0.2.0¶
Extraction de données structurées depuis factures, reçus, CVs, IDs et RIBs
Endpoints¶
GET /health¶
Health
Description
Health check endpoint.
Responses
GET /v1/schemas¶
Schemas
Description
List all supported document schemas with their fields.
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
X-API-Key |
header | No |
Responses
POST /v1/extract¶
Extract
Description
Extract structured data from a single uploaded file.
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
document_type |
query | No | |||
X-API-Key |
header | No |
Request body
Responses
{
"document_type": "invoice",
"data": null,
"text_source": null,
"raw_text": "string",
"confidence": 10.12,
"field_confidences": [
{
"field": "string",
"score": 10.12,
"reason": null
}
],
"pii_detected": null,
"error": null
}
Schema of the response body
{
"properties": {
"document_type": {
"$ref": "#/components/schemas/DocumentType"
},
"data": {
"anyOf": [
{
"$ref": "#/components/schemas/InvoiceData"
},
{
"$ref": "#/components/schemas/ReceiptData"
},
{
"$ref": "#/components/schemas/ResumeData"
},
{
"$ref": "#/components/schemas/IDCardData"
},
{
"$ref": "#/components/schemas/RibData"
},
{
"type": "null"
}
],
"title": "Data"
},
"text_source": {
"anyOf": [
{
"$ref": "#/components/schemas/TextSource"
},
{
"type": "null"
}
]
},
"raw_text": {
"type": "string",
"title": "Raw Text"
},
"confidence": {
"type": "number",
"maximum": 1.0,
"minimum": 0.0,
"title": "Confidence"
},
"field_confidences": {
"items": {
"$ref": "#/components/schemas/FieldConfidence"
},
"type": "array",
"title": "Field Confidences"
},
"pii_detected": {
"anyOf": [
{
"items": {
"additionalProperties": true,
"type": "object"
},
"type": "array"
},
{
"type": "null"
}
],
"title": "Pii Detected"
},
"error": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Error"
}
},
"type": "object",
"required": [
"document_type",
"raw_text",
"confidence"
],
"title": "ExtractionResult"
}
POST /v1/extract/batch¶
Extract Batch
Description
Extract structured data from multiple uploaded files.
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
document_type |
query | No | |||
X-API-Key |
header | No |
Request body
Responses
[
{
"document_type": "invoice",
"data": null,
"text_source": null,
"raw_text": "string",
"confidence": 10.12,
"field_confidences": [
{
"field": "string",
"score": 10.12,
"reason": null
}
],
"pii_detected": null,
"error": null
}
]
GET /v1/receipt-policies¶
Get Receipt Policy V1
Description
Politique de dépenses du tenant authentifié, ou la politique par défaut.
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
X-API-Key |
header | No |
Responses
{
"currency": "string",
"default_max_amount": 10.12,
"caps": [
{
"category": "string",
"max_amount": 10.12
}
],
"require_vat_above": null,
"is_default": true
}
Schema of the response body
{
"properties": {
"currency": {
"type": "string",
"maxLength": 3,
"minLength": 3,
"title": "Currency",
"default": "EUR"
},
"default_max_amount": {
"type": "number",
"minimum": 0.0,
"title": "Default Max Amount",
"default": 1000.0
},
"caps": {
"items": {
"$ref": "#/components/schemas/ExpenseCap"
},
"type": "array",
"title": "Caps"
},
"require_vat_above": {
"anyOf": [
{
"type": "number",
"minimum": 0.0
},
{
"type": "null"
}
],
"title": "Require Vat Above"
},
"is_default": {
"type": "boolean",
"title": "Is Default",
"default": false
}
},
"type": "object",
"title": "ExpensePolicyOut",
"description": "Politique renvoyée par l'API, avec l'indication qu'aucune n'a été enregistrée."
}
PUT /v1/receipt-policies¶
Put Receipt Policy V1
Description
Remplace la politique du tenant authentifié.
Le tenant vient de l'authentification (ADR-001) ; un tenant_id présent
dans le corps est ignoré, jamais honoré.
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
X-API-Key |
header | No |
Request body
{
"currency": "string",
"default_max_amount": 10.12,
"caps": [
{
"category": "string",
"max_amount": 10.12
}
],
"require_vat_above": null
}
Schema of the request body
{
"properties": {
"currency": {
"type": "string",
"maxLength": 3,
"minLength": 3,
"title": "Currency",
"default": "EUR"
},
"default_max_amount": {
"type": "number",
"minimum": 0.0,
"title": "Default Max Amount",
"default": 1000.0
},
"caps": {
"items": {
"$ref": "#/components/schemas/ExpenseCap"
},
"type": "array",
"title": "Caps"
},
"require_vat_above": {
"anyOf": [
{
"type": "number",
"minimum": 0.0
},
{
"type": "null"
}
],
"title": "Require Vat Above"
}
},
"type": "object",
"title": "ExpensePolicy",
"description": "Politique de dépenses d'un tenant.\n\nLe verdict produit à partir de cette politique est une **évaluation**,\njamais une autorisation : doc-extract ne refuse aucun remboursement, il\nrapporte qu'un montant dépasse un plafond déclaré."
}
Responses
{
"currency": "string",
"default_max_amount": 10.12,
"caps": [
{
"category": "string",
"max_amount": 10.12
}
],
"require_vat_above": null,
"is_default": true
}
Schema of the response body
{
"properties": {
"currency": {
"type": "string",
"maxLength": 3,
"minLength": 3,
"title": "Currency",
"default": "EUR"
},
"default_max_amount": {
"type": "number",
"minimum": 0.0,
"title": "Default Max Amount",
"default": 1000.0
},
"caps": {
"items": {
"$ref": "#/components/schemas/ExpenseCap"
},
"type": "array",
"title": "Caps"
},
"require_vat_above": {
"anyOf": [
{
"type": "number",
"minimum": 0.0
},
{
"type": "null"
}
],
"title": "Require Vat Above"
},
"is_default": {
"type": "boolean",
"title": "Is Default",
"default": false
}
},
"type": "object",
"title": "ExpensePolicyOut",
"description": "Politique renvoyée par l'API, avec l'indication qu'aucune n'a été enregistrée."
}
POST /v1/extract/receipts/analyze¶
Analyze Receipts
Description
Analyse un lot de reçus : doublons, écarts de politique, cohérence, authenticité.
Pour chaque reçu : extraction (document_type="receipt"), empreinte
perceptuelle, contrôle de cohérence interne, confrontation à la
politique de dépenses du tenant, et avis d'authenticité de doc-trust
(la clé de l'appelant lui est transmise telle quelle, ADR-023 ; au plus
4 appels simultanés par lot). Puis, sur l'ensemble du lot,
rapprochement des doublons.
findings et authenticity sont des évaluations, jamais des
autorisations : cette route ne bloque rien, ne rejette aucun reçu pour
cause de constat, et ne modifie aucune politique. Un fichier illisible,
trop volumineux, ou dont l'extraction échoue, produit un item avec
error renseigné et n'interrompt jamais le lot — même contrat que
/v1/extract/batch. Seul un problème de requête (lot vide, lot
au-delà de MAX_BATCH_RECEIPTS) fait échouer l'appel entier.
authenticity vaut null quand l'avis n'a pas pu être obtenu (doc-trust
indisponible, format non pris en charge) : « non évalué », jamais « aucun
signal ». Cet échec n'interrompt pas le lot.
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
X-API-Key |
header | string | No |
Request body
Schema of the request body
{
"properties": {
"files": {
"items": {
"type": "string",
"contentMediaType": "application/octet-stream"
},
"type": "array",
"title": "Files"
},
"categories": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Categories"
}
},
"type": "object",
"required": [
"files"
],
"title": "Body_analyze_receipts_v1_extract_receipts_analyze_post"
}
Responses
{
"items": [
{
"index": 0,
"filename": "string",
"data": null,
"confidence": null,
"findings": [
{
"code": "string",
"message": "string",
"severity": "info"
}
],
"authenticity": null,
"error": null
}
],
"duplicate_groups": [
{
"indices": [
0
],
"rule": "fingerprint"
}
],
"analysed_at": "2022-04-13T15:42:05.901Z",
"policy_is_default": true
}
Schema of the response body
{
"properties": {
"items": {
"items": {
"$ref": "#/components/schemas/ReceiptAnalysisItem"
},
"type": "array",
"title": "Items"
},
"duplicate_groups": {
"items": {
"$ref": "#/components/schemas/DuplicateGroupOut"
},
"type": "array",
"title": "Duplicate Groups"
},
"analysed_at": {
"type": "string",
"format": "date-time",
"title": "Analysed At"
},
"policy_is_default": {
"type": "boolean",
"title": "Policy Is Default"
}
},
"type": "object",
"required": [
"items",
"analysed_at",
"policy_is_default"
],
"title": "ReceiptAnalysisResponse",
"description": "Réponse de l'analyse du lot.\n\nNe bloque rien, ne rejette aucun reçu pour cause de constat, et ne\nmodifie aucune politique de dépenses."
}
Schemas¶
AuthenticityOut¶
| Name | Type | Description |
|---|---|---|
explanation |
string | |
rules_version |
string | |
score |
number | |
signals |
Array<FindingOut> | |
verdict |
string |
Body_analyze_receipts_v1_extract_receipts_analyze_post¶
| Name | Type | Description |
|---|---|---|
categories |
||
files |
Array<string> |
Body_extract_batch_v1_extract_batch_post¶
| Name | Type | Description |
|---|---|---|
files |
Array<string> |
Body_extract_v1_extract_post¶
| Name | Type | Description |
|---|---|---|
file |
string |
Certification¶
| Name | Type | Description |
|---|---|---|
name |
||
provider |
||
year |
Diploma¶
| Name | Type | Description |
|---|---|---|
degree |
||
end_date |
||
field_of_study |
||
school |
||
start_date |
DocumentType¶
Type: string
DuplicateGroupOut¶
| Name | Type | Description |
|---|---|---|
indices |
Array<integer> | |
rule |
string |
ExpenseCap¶
| Name | Type | Description |
|---|---|---|
category |
string | |
max_amount |
number |
ExpensePolicy¶
| Name | Type | Description |
|---|---|---|
caps |
Array<ExpenseCap> | |
currency |
string | |
default_max_amount |
number | |
require_vat_above |
ExpensePolicyOut¶
| Name | Type | Description |
|---|---|---|
caps |
Array<ExpenseCap> | |
currency |
string | |
default_max_amount |
number | |
is_default |
boolean | |
require_vat_above |
Experience¶
| Name | Type | Description |
|---|---|---|
company |
||
description |
||
end_date |
YYYY-MM, YYYY, or 'Present' | |
location |
||
start_date |
YYYY-MM or YYYY | |
title |
ExtractionResult¶
| Name | Type | Description |
|---|---|---|
confidence |
number | |
data |
||
document_type |
DocumentType | |
error |
||
field_confidences |
Array<FieldConfidence> | |
pii_detected |
||
raw_text |
string | |
text_source |
FieldConfidence¶
| Name | Type | Description |
|---|---|---|
field |
string | |
reason |
||
score |
number |
FindingOut¶
| Name | Type | Description |
|---|---|---|
code |
string | |
message |
string | |
severity |
string |
HealthResponse¶
| Name | Type | Description |
|---|---|---|
status |
string | |
version |
string |
HTTPValidationError¶
| Name | Type | Description |
|---|---|---|
detail |
Array<ValidationError> |
IDCardData¶
| Name | Type | Description |
|---|---|---|
birth_date |
||
birth_place |
||
document_number |
||
expiry_date |
||
first_name |
||
gender |
M or F | |
issue_date |
||
last_name |
||
nationality |
InvoiceData¶
| Name | Type | Description |
|---|---|---|
currency |
ISO 4217 code (EUR, USD, ...) | |
due_date |
||
invoice_date |
||
invoice_number |
||
line_items |
Array<LineItem> | |
payment_method |
||
payment_reference |
IBAN or payment reference | |
subtotal |
||
total_amount |
||
vat_amount |
||
vendor_address |
||
vendor_name |
||
vendor_tax_id |
SIRET / tax ID |
LineItem¶
| Name | Type | Description |
|---|---|---|
amount |
||
description |
||
quantity |
||
unit_price |
||
vat_rate |
ReceiptAnalysisItem¶
| Name | Type | Description |
|---|---|---|
authenticity |
Signaux d'authenticité faibles et contournables — une aide au tri humain, jamais une preuve ni un verdict. `null` signifie que l'authenticité n'a pas pu être évaluée (fichier rejeté avant analyse, ou échec du fournisseur de signaux), jamais qu'aucun signal n'a été trouvé : l'absence de signal se lit sur un objet présent dont `signals` est vide. | |
confidence |
||
data |
||
error |
||
filename |
string | |
findings |
Array<FindingOut> | Écarts de cohérence interne et de politique de dépenses. Une évaluation, jamais une autorisation : aucun reçu n'est rejeté. |
index |
integer |
ReceiptAnalysisResponse¶
| Name | Type | Description |
|---|---|---|
analysed_at |
string(date-time) | |
duplicate_groups |
Array<DuplicateGroupOut> | |
items |
Array<ReceiptAnalysisItem> | |
policy_is_default |
boolean |
ReceiptData¶
| Name | Type | Description |
|---|---|---|
currency |
||
line_items |
Array<LineItem> | |
payment_method |
||
receipt_date |
||
receipt_time |
Heure du ticket, si imprimée | |
subtotal |
||
total_amount |
||
vat_amount |
||
vat_breakdown |
Array<VatBreakdownEntry> | Ventilation par taux ; vat_amount reste le total |
vendor_address |
||
vendor_name |
ResumeData¶
| Name | Type | Description |
|---|---|---|
certifications |
Array<Certification> | |
diplomas |
Array<Diploma> | |
email |
||
experiences |
Array<Experience> | |
firstname |
||
languages |
Array<string> | |
lastname |
||
linkedin |
||
location |
||
phone |
||
skills |
Array<string> |
RibData¶
| Name | Type | Description |
|---|---|---|
account_number |
Numéro de compte (11 chars) | |
agency_code |
Code guichet (5 digits) | |
bank_address |
Domiciliation / agency address | |
bank_name |
||
bic |
||
holder_name |
Account holder as printed (titulaire) | |
iban |
IBAN without spaces | |
rib_key |
Clé RIB (2 digits) |
SchemaField¶
| Name | Type | Description |
|---|---|---|
description |
||
name |
string | |
type |
string |
SchemaInfo¶
| Name | Type | Description |
|---|---|---|
document_type |
string | |
fields |
Array<SchemaField> |
TextSource¶
Type: string
ValidationError¶
| Name | Type | Description |
|---|---|---|
ctx |
||
input |
||
loc |
Array<> | |
msg |
string | |
type |
string |
VatBreakdownEntry¶
| Name | Type | Description |
|---|---|---|
amount |
Montant de TVA pour ce taux | |
rate |
Taux en pourcentage, ex. 20.0 ou 5.5 | |
taxable_base |
Base imposable pour ce taux |