Skip to content

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

curl -X POST http://localhost:30002/v1/extract \
  -H "X-API-Key: your-local-key" \
  -F "file=@resume.pdf" \
  -F "document_type=resume"
curl -X POST https://api.dev.djinn.solyntek.io/v1/extract \
  -H "X-API-Key: your-api-key" \
  -F "file=@resume.pdf" \
  -F "document_type=resume"
{
  "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

{
    "status": "string",
    "version": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "properties": {
        "status": {
            "type": "string",
            "title": "Status",
            "default": "ok"
        },
        "version": {
            "type": "string",
            "title": "Version",
            "default": "0.2.0"
        }
    },
    "type": "object",
    "title": "HealthResponse",
    "description": "Response model for the health check endpoint."
}

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

[
    {
        "document_type": "string",
        "fields": [
            {
                "name": "string",
                "type": "string",
                "description": null
            }
        ]
    }
]
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "array",
    "items": {
        "$ref": "#/components/schemas/SchemaInfo"
    },
    "title": "Response Schemas V1 Schemas Get"
}

{
    "detail": [
        {
            "loc": [
                null
            ],
            "msg": "string",
            "type": "string",
            "input": null,
            "ctx": {}
        }
    ]
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "properties": {
        "detail": {
            "items": {
                "$ref": "#/components/schemas/ValidationError"
            },
            "type": "array",
            "title": "Detail"
        }
    },
    "type": "object",
    "title": "HTTPValidationError"
}

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

{
    "file": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the request body
{
    "properties": {
        "file": {
            "type": "string",
            "contentMediaType": "application/octet-stream",
            "title": "File"
        }
    },
    "type": "object",
    "required": [
        "file"
    ],
    "title": "Body_extract_v1_extract_post"
}

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
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

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"
}

{
    "detail": [
        {
            "loc": [
                null
            ],
            "msg": "string",
            "type": "string",
            "input": null,
            "ctx": {}
        }
    ]
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "properties": {
        "detail": {
            "items": {
                "$ref": "#/components/schemas/ValidationError"
            },
            "type": "array",
            "title": "Detail"
        }
    },
    "type": "object",
    "title": "HTTPValidationError"
}

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

{
    "files": [
        "string"
    ]
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the request body
{
    "properties": {
        "files": {
            "items": {
                "type": "string",
                "contentMediaType": "application/octet-stream"
            },
            "type": "array",
            "title": "Files"
        }
    },
    "type": "object",
    "required": [
        "files"
    ],
    "title": "Body_extract_batch_v1_extract_batch_post"
}

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
    }
]
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "array",
    "items": {
        "$ref": "#/components/schemas/ExtractionResult"
    },
    "title": "Response Extract Batch V1 Extract Batch Post"
}

{
    "detail": [
        {
            "loc": [
                null
            ],
            "msg": "string",
            "type": "string",
            "input": null,
            "ctx": {}
        }
    ]
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "properties": {
        "detail": {
            "items": {
                "$ref": "#/components/schemas/ValidationError"
            },
            "type": "array",
            "title": "Detail"
        }
    },
    "type": "object",
    "title": "HTTPValidationError"
}

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
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

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."
}

{
    "detail": [
        {
            "loc": [
                null
            ],
            "msg": "string",
            "type": "string",
            "input": null,
            "ctx": {}
        }
    ]
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "properties": {
        "detail": {
            "items": {
                "$ref": "#/components/schemas/ValidationError"
            },
            "type": "array",
            "title": "Detail"
        }
    },
    "type": "object",
    "title": "HTTPValidationError"
}

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
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

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
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

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."
}

{
    "detail": [
        {
            "loc": [
                null
            ],
            "msg": "string",
            "type": "string",
            "input": null,
            "ctx": {}
        }
    ]
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "properties": {
        "detail": {
            "items": {
                "$ref": "#/components/schemas/ValidationError"
            },
            "type": "array",
            "title": "Detail"
        }
    },
    "type": "object",
    "title": "HTTPValidationError"
}

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

{
    "files": [
        "string"
    ],
    "categories": null
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

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
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

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."
}

{
    "detail": [
        {
            "loc": [
                null
            ],
            "msg": "string",
            "type": "string",
            "input": null,
            "ctx": {}
        }
    ]
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "properties": {
        "detail": {
            "items": {
                "$ref": "#/components/schemas/ValidationError"
            },
            "type": "array",
            "title": "Detail"
        }
    },
    "type": "object",
    "title": "HTTPValidationError"
}

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