Documentation · API v1
API pour développeurs
Envoyez un PDF à l’API Veritas-Doc et recevez le même relevé d’indices techniques de modification que dans l’interface web : en JSON pour vos intégrations, ou en relevé PDF annoté à archiver. Deux endpoints, une clé d’accès, aucun SDK à installer.
Sur cette page
Introduction
L’API permet d’intégrer l’analyse Veritas-Doc à un logiciel métier, à une GED ou à un script : votre serveur envoie un PDF et l’API répond avec le relevé complet, au même format que celui affiché dans l’interface (statut, score de confiance, outil d’édition détecté, indices relevés, zones et texte retrouvé).
Le résultat est un relevé d’indices techniques de modification, pas un verdict. Un indice ne prouve pas une fraude et n’établit pas qu’un document est authentique : un document réenregistré pour une raison légitime produit les mêmes indices. L’interprétation reste à la charge de la personne qui lit le relevé.
Le JSON est le format par défaut, pensé pour les intégrations. En option, l’API renvoie le relevé sous la forme d’un PDF annoté : une page de synthèse, puis les pages du document d’origine avec les zones repérées encadrées, à archiver au dossier (voir Relevé PDF annoté).
Transmettez le champ disclaimer à vos utilisateurs
Chaque réponse contient un champ disclaimer qui rappelle la portée du relevé. Le texte doit être transmis, tel quel, aux utilisateurs finaux qui consultent le résultat dans votre produit.
En bref
- URL de base
https://veritas-doc.techstride.app/api/v1- Accès
- Abonnement Cabinet actif, ou offre Entreprise
- Authentification
- Clé d’accès dans l’en-tête
Authorization: Bearer - Formats
- Requête en PDF brut ou en
multipart/form-data; réponse en JSON (par défaut) ou en relevé PDF annoté (format=pdf) - Usage
- Appels de serveur à serveur uniquement (aucun en-tête CORS)
- Version
- v1, annoncée par l’en-tête
X-Veritas-API-Version: 1
Démarrage rapide
Trois étapes suffisent pour obtenir un premier relevé.
Obtenir une clé d’accès
Connectez-vous, ouvrez Mon compte et créez une clé dans la section consacrée à l’API. Donnez-lui un nom qui identifie son usage. La clé n’est affichée qu’une seule fois : copiez-la aussitôt dans votre gestionnaire de secrets. L’accès à l’API demande un abonnement Cabinet actif ou l’offre Entreprise (voir les offres).
Faire un premier appel
Envoyez un PDF avec
curl. Pour vérifier seulement que votre clé fonctionne, sans consommer d’analyse, appelez d’abord GET /api/v1/usage.curl -X POST "https://veritas-doc.techstride.app/api/v1/analyze" \ -H "Authorization: Bearer vd_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \ -H "Content-Type: application/pdf" \ --data-binary @document.pdfLire la réponse
La réponse est le relevé, en JSON. Lisez d’abord
status,confidence_scoreetfindings, puis transmettezdisclaimerà vos utilisateurs. Chaque champ est décrit dans la section Réponse. Pour obtenir un PDF à archiver plutôt que du JSON, ajoutez?format=pdf(voir Relevé PDF annoté).
Authentification
Toutes les routes /api/v1/* attendent une clé d’accès dans l’en-tête Authorization, au format Bearer.
Authorization: Bearer vd_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXUne clé commence par vd_live_ et compte 51 caractères au total. Veritas-Doc n’en conserve pas le texte : elle n’est montrée qu’une seule fois, à la création. En cas de perte, révoquez-la et créez-en une autre.
L’API n’utilise ni cookie ni session. Elle n’envoie aucun en-tête CORS : elle est conçue pour des appels de serveur à serveur, et une clé ne doit jamais être embarquée dans une page web ou dans une application distribuée.
Les droits d’accès sont vérifiés à chaque appel.
Erreurs d’authentification
| Code | HTTP | Cas |
|---|---|---|
MISSING_API_KEY | 401 | En-tête absent, ou clé mal formée. |
INVALID_API_KEY | 401 | Clé inconnue, révoquée ou expirée. Le message est identique dans les trois cas, pour ne rien révéler. |
API_ACCESS_NOT_ALLOWED | 403 | La clé est valide, mais le compte n’a pas accès à l’API (ni abonnement Cabinet actif, ni offre Entreprise). |
Analyser un PDF
POST/api/v1/analyze
Envoie un PDF et renvoie son relevé, en JSON (par défaut) ou en relevé PDF annoté (`format=pdf`). Deux formats de corps sont acceptés.
Corps de la requête
| Format | Content-Type | Corps |
|---|---|---|
| PDF brut | application/pdf | Les octets du fichier, tels quels. |
| Formulaire | multipart/form-data | Un champ nommé file qui contient le PDF. |
Paramètre include_previews
Par défaut, l’aperçu image de chaque page (pages[].preview) est retiré de la réponse, pour la garder légère. Ajoutez ?include_previews=true à l’URL pour le conserver, par exemple POST /api/v1/analyze?include_previews=true. Ne le demandez que si vous affichez les pages.
Paramètre format
?format=json (par défaut) ou ?format=pdf. Vous pouvez aussi envoyer Accept: application/pdf ; si les deux sont présents, le paramètre l’emporte. Toute autre valeur donne 400 BAD_REQUEST, et rien n’est décompté. Le relevé PDF est décrit dans Relevé PDF annoté.
Limites
- Taille : les documents de toute taille sont acceptés. Au-delà de 4,5 Mo, la plateforme actuelle peut refuser l’envoi avant qu’il n’atteigne l’API : ce point disparaîtra avec le changement d’hébergement.
- Pages : aucune limite de nombre de pages. Le champ
limits.truncatedindique une analyse partielle : lisez-le avant de conclure. - Contenu : le fichier doit commencer par
%PDF-, sinon l’API répond415 NOT_A_PDF. - Décompte : une analyse consomme comme dans l’interface web (voir Quotas). Les erreurs liées au document (
422), les pannes du service d’analyse (502) et un relevé PDF trop lourd (413 REPORT_TOO_LARGE) ne sont pas décomptés.
Exemples de code
Les exemples Node.js de cette page demandent Node.js 18 ou plus (fetch est intégré) et sont écrits en module ES : enregistrez-les dans un fichier .mjs, ou ajoutez "type": "module" à votre package.json.
Exemple : PDF brut
Le corps est le fichier lui-même. Avec curl, utilisez --data-binary (et non -d, qui altère les octets).
curl -X POST "https://veritas-doc.techstride.app/api/v1/analyze" \
-H "Authorization: Bearer vd_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
-H "Content-Type: application/pdf" \
--data-binary @document.pdfimport requests
API_KEY = "vd_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
ENDPOINT = "https://veritas-doc.techstride.app/api/v1/analyze"
with open("document.pdf", "rb") as pdf:
response = requests.post(
ENDPOINT,
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/pdf",
},
data=pdf,
timeout=120,
)
if not response.ok:
raise SystemExit(f"{response.status_code} {response.text[:200]}")
body = response.json()
print(body["status"], body["confidence_score"])
print(body["disclaimer"])import { readFile } from "node:fs/promises";
const API_KEY = "vd_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
const ENDPOINT = "https://veritas-doc.techstride.app/api/v1/analyze";
const pdf = await readFile("document.pdf");
const response = await fetch(ENDPOINT, {
method: "POST",
headers: {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/pdf",
},
body: pdf,
});
if (!response.ok) {
console.error(response.status, (await response.text()).slice(0, 200));
process.exit(1);
}
const body = await response.json();
console.log(body.status, body.confidence_score);
console.log(body.disclaimer);Exemple : formulaire multipart
Ne définissez pas vous-même l’en-tête Content-Type : le client y ajoute la frontière (boundary) du formulaire.
curl -X POST "https://veritas-doc.techstride.app/api/v1/analyze" \
-H "Authorization: Bearer vd_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
-F "file=@document.pdf;type=application/pdf"import requests
API_KEY = "vd_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
ENDPOINT = "https://veritas-doc.techstride.app/api/v1/analyze"
with open("document.pdf", "rb") as pdf:
response = requests.post(
ENDPOINT,
headers={"Authorization": f"Bearer {API_KEY}"},
files={"file": ("document.pdf", pdf, "application/pdf")},
timeout=120,
)
if not response.ok:
raise SystemExit(f"{response.status_code} {response.text[:200]}")
body = response.json()
print(body["status"], body["confidence_score"])
print(body["disclaimer"])import { readFile } from "node:fs/promises";
const API_KEY = "vd_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
const ENDPOINT = "https://veritas-doc.techstride.app/api/v1/analyze";
const pdf = await readFile("document.pdf");
const form = new FormData();
form.append("file", new Blob([pdf], { type: "application/pdf" }), "document.pdf");
const response = await fetch(ENDPOINT, {
method: "POST",
headers: { Authorization: `Bearer ${API_KEY}` },
body: form,
});
if (!response.ok) {
console.error(response.status, (await response.text()).slice(0, 200));
process.exit(1);
}
const body = await response.json();
console.log(body.status, body.confidence_score);
console.log(body.disclaimer);Réponse
Une réponse 200 contient le relevé complet. Le tableau décrit les champs principaux. La réponse peut contenir d’autres champs : ignorez ceux que vous ne connaissez pas (voir Versionnage).
Champs principaux
| Champ | Type | Description |
|---|---|---|
analysis_id | uuid | Identifiant de l’analyse. |
usage.source | string | Ce qui a été décompté : cabinet (abonnement), free (analyse gratuite), credit (crédit), ou repeat (même fichier renvoyé dans la fenêtre de relance de 10 minutes : rien n’est décompté). |
schema_version | string | Version du format du relevé, par exemple 2.0. |
status | string | Lecture synthétique : CONFORME (aucun indice sérieux : score d’au moins 80 et aucun indice de sévérité high), SUSPECT (score de 50 à 79, ou présence d’un indice de sévérité high) ou ALTERE (score inférieur à 50). Un relevé CONFORME peut encore lister des indices mineurs : lisez findings pour le détail. Ce n’est pas un verdict. |
confidence_score | integer | Score de confiance de 0 à 100. Chaque indice relevé en retire son poids : plus le score est bas, plus les indices sont nombreux ou marqués. |
risk_level | string | Niveau d’attention : low, medium ou high. |
document_info | object | Métadonnées du fichier : author, creator, producer, title, creation_date, modification_date, pdf_version, page_count, pages_analyzed, encrypted, has_xmp. Une valeur absente du fichier vaut null. |
editor_assessment | object | Outil d’édition détecté : detected_tool, category (desktop_pro, office, online_editor, image_editor, scanner, generator ou unknown), trust_score (0 à 100), creator_producer_mismatch (booléen) et rationale. |
findings[] | array | Indices relevés. Chacun a un code stable, une severity (info, low, medium ou high), un weight (points retirés au score), un title, un detail et la page concernée, ou null. |
pages[] | array | Une entrée par page analysée : index (à partir de 0), width, height, zones[] et, sur demande seulement, preview. |
pages[].zones[] | array | Zones relevées sur la page : id, type (ink, white_rect, redaction, image_overlay ou annotation), severity, rect et, si un texte a pu être retrouvé sous la zone, hidden_text et hidden_text_confidence (0 à 1). |
pages[].zones[].rect | object | Position de la zone : x, y, w, h, tous normalisés entre 0 et 1 par rapport aux dimensions de la page. |
limits | object | max_pages (nombre maximal de pages analysées, null s’il n’y a pas de limite) et truncated (true si l’analyse est partielle : lisez-le avant de conclure). |
disclaimer | string | Rappel de la portée du relevé. À transmettre aux utilisateurs finaux. |
Exemple de réponse
Réponse abrégée pour un document de deux pages. Les textes title, detail et rationale sont destinés à être lus par des personnes et sont actuellement rédigés en français : appuyez votre logique sur code, severity, category et status.
{
"analysis_id": "3f6b1c1e-8d0a-4b52-9a57-2d1e5f0c7a44",
"usage": { "source": "cabinet" },
"schema_version": "2.0",
"status": "SUSPECT",
"confidence_score": 55,
"risk_level": "medium",
"document_info": {
"author": null,
"creator": "Microsoft Word",
"producer": "Adobe Acrobat Pro",
"title": null,
"creation_date": "2026-03-02T09:14:00+01:00",
"modification_date": "2026-03-04T17:41:00+01:00",
"pdf_version": "1.7",
"page_count": 2,
"pages_analyzed": 2,
"encrypted": false,
"has_xmp": true
},
"editor_assessment": {
"detected_tool": "Adobe Acrobat",
"category": "desktop_pro",
"trust_score": 25,
"creator_producer_mismatch": true,
"rationale": "Créé avec Microsoft Word, produit par Adobe Acrobat. Modificateurs appliqués : mismatch Creator/Producer (-20), ModDate postérieure à CreationDate (-10)."
},
"findings": [
{
"code": "CREATOR_PRODUCER_MISMATCH",
"severity": "high",
"weight": 20,
"title": "L'outil de création et l'outil de production diffèrent",
"detail": "Créé avec Microsoft Word, produit par Adobe Acrobat. Modificateurs appliqués : mismatch Creator/Producer (-20), ModDate postérieure à CreationDate (-10).",
"page": null
},
{
"code": "INCREMENTAL_SAVE",
"severity": "medium",
"weight": 15,
"title": "Le document a été enregistré 2 fois",
"detail": "2 marqueurs startxref détectés. Un enregistrement incrémental conserve les versions précédentes dans le fichier.",
"page": null
},
{
"code": "MODDATE_AFTER_CREATION",
"severity": "medium",
"weight": 10,
"title": "Le document a été modifié après sa création",
"detail": "Création : 2026-03-02T09:14:00+01:00 — modification : 2026-03-04T17:41:00+01:00.",
"page": null
}
],
"pages": [
{ "index": 0, "width": 595.28, "height": 841.89, "zones": [] },
{ "index": 1, "width": 595.28, "height": 841.89, "zones": [] }
],
"limits": { "max_pages": null, "truncated": false },
"disclaimer": "Indices techniques. Ne constitue pas une preuve de fraude. Un document peut présenter ces indices pour des raisons parfaitement légitimes (ré-enregistrement, signature, export)."
}Exemple de zone
Quand un rectangle recouvre du texte, la zone peut porter le texte retrouvé dessous. Cet exemple est illustratif.
{
"id": "p0z1",
"type": "white_rect",
"rect": { "x": 0.12, "y": 0.33, "w": 0.2, "h": 0.05 },
"severity": "high",
"hidden_text": "Solde : 12 480,00 €",
"hidden_text_confidence": 0.82
}En-têtes de réponse
Les réponses de succès portent ces en-têtes. Conservez X-Request-Id : il identifie votre requête en cas de question au support.
| En-tête | Description |
|---|---|
X-Veritas-API-Version | Version de l’API, ici 1. |
X-Request-Id | Identifiant de la requête. |
X-RateLimit-Limit | Nombre de requêtes autorisées par minute pour cette clé. |
X-RateLimit-Remaining | Requêtes restantes dans la fenêtre en cours. |
Cache-Control | Toujours no-store : ne mettez pas ces réponses en cache. |
Relevé PDF annoté
Le JSON est le format par défaut. Pour archiver le résultat au dossier ou le remettre à une personne, demandez le relevé PDF annoté : les pages du document d’origine avec les zones repérées encadrées, précédées d’une page de synthèse. C’est le même relevé d’indices techniques de modification, présenté pour la lecture.
Ce que contient le relevé
- Une page de synthèse : statut, score de confiance, indices relevés (nom, sévérité, poids), fiche du document, empreinte SHA-256 du fichier analysé et avertissement.
- Les pages analysées du document d’origine, avec chaque zone repérée encadrée et numérotée. La couleur du cadre dépend de la sévérité.
- Après chaque page annotée, une page de légende : type de zone, position et, quand il existe, le texte retrouvé sous la zone.
Le contenu d’origine n’est pas modifié : les repères sont dessinés par-dessus, dans un calque que votre lecteur PDF peut masquer. Seules les pages analysées figurent dans le relevé ; si l’analyse est partielle, la synthèse le dit.
Demander le relevé
Ajoutez ?format=pdf à l’URL, ou envoyez Accept: application/pdf. Le corps de la réponse est le fichier : enregistrez-le tel quel (avec curl, -o releve.pdf). Le corps de la requête est le même que pour le JSON.
curl -X POST "https://veritas-doc.techstride.app/api/v1/analyze?format=pdf" \
-H "Authorization: Bearer vd_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
-H "Content-Type: application/pdf" \
--data-binary @document.pdf \
-o releve.pdf -w "HTTP %{http_code}\n"import requests
API_KEY = "vd_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
ENDPOINT = "https://veritas-doc.techstride.app/api/v1/analyze"
with open("document.pdf", "rb") as pdf:
response = requests.post(
ENDPOINT,
params={"format": "pdf"},
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/pdf",
},
data=pdf,
timeout=120,
)
if not response.ok:
# Errors are always JSON, even with format=pdf.
raise SystemExit(f"{response.status_code} {response.text[:200]}")
with open("releve.pdf", "wb") as report:
report.write(response.content)
print(response.headers["X-Veritas-Status"], response.headers.get("X-Veritas-Score"))import { readFile, writeFile } from "node:fs/promises";
const API_KEY = "vd_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
const ENDPOINT = "https://veritas-doc.techstride.app/api/v1/analyze?format=pdf";
const pdf = await readFile("document.pdf");
const response = await fetch(ENDPOINT, {
method: "POST",
headers: {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/pdf",
},
body: pdf,
});
if (!response.ok) {
// Errors are always JSON, even with format=pdf.
console.error(response.status, (await response.text()).slice(0, 200));
process.exit(1);
}
await writeFile("releve.pdf", Buffer.from(await response.arrayBuffer()));
console.log(response.headers.get("X-Veritas-Status"), response.headers.get("X-Veritas-Score"));Réponse
Une réponse 200 a pour Content-Type application/pdf et pour corps le relevé, proposé en pièce jointe sous le nom releve-veritas- suivi des 8 premiers caractères de l’empreinte du fichier. Il n’y a pas de corps JSON : l’essentiel du verdict est repris dans les en-têtes ci-dessous, qui s’ajoutent à ceux de Réponse.
En-têtes du relevé
| En-tête | Description |
|---|---|
Content-Disposition | Pièce jointe nommée releve-veritas-<8 caractères>.pdf. |
X-Veritas-Status | Équivalent de status : CONFORME, SUSPECT ou ALTERE. |
X-Veritas-Score | Équivalent de confidence_score, de 0 à 100. |
X-Veritas-Analysis-Id | Équivalent de analysis_id. |
X-Veritas-Usage-Source | Équivalent de usage.source : cabinet, free, credit ou repeat. |
X-Veritas-Truncated | Équivalent de limits.truncated : true si l’analyse est partielle. |
Exemple d’en-têtes
HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Disposition: attachment; filename="releve-veritas-3f6b1c1e.pdf"
X-Veritas-API-Version: 1
X-Veritas-Status: SUSPECT
X-Veritas-Score: 55
X-Veritas-Analysis-Id: 3f6b1c1e-8d0a-4b52-9a57-2d1e5f0c7a44
X-Veritas-Usage-Source: cabinet
X-Veritas-Truncated: false
Cache-Control: no-storeErreurs
Les erreurs restent en JSON, au format décrit dans Erreurs : testez le statut HTTP avant de traiter le corps comme un PDF. Un relevé plus lourd que la taille de réponse maximale donne 413 REPORT_TOO_LARGE : rien n’est décompté, et vous pouvez redemander le même fichier avec format=json.
Exemple de réponse 413 pour un relevé trop lourd :
{
"error": {
"code": "REPORT_TOO_LARGE",
"message": "Le relevé PDF annoté dépasse la taille maximale de réponse. Utilisez format=json ou réduisez le document."
}
}Taille
Le relevé pèse à peu près le poids des pages analysées du document d’origine, plus quelques dizaines de Ko. Au-delà de 4 Mo, l’API répond 413 REPORT_TOO_LARGE. Cette limite vient de la taille de réponse acceptée par la plateforme actuelle : elle disparaîtra avec le changement d’hébergement.
Décompte
Comme pour le JSON : une analyse est décomptée par appel réussi, et rien ne l’est en cas d’échec, y compris REPORT_TOO_LARGE (un relevé qui n’est pas livré n’est pas facturé). Redemander le même fichier dans les 10 minutes, dans l’autre format, est une relance : X-Veritas-Usage-Source: repeat, rien n’est décompté de nouveau.
JSON ou PDF ?
| Critère | JSON (par défaut) | Relevé PDF annoté |
|---|---|---|
| Usage | Intégration logicielle, traitement automatique, tableau de bord. | Archivage au dossier, remise à une personne, lecture humaine. |
| Contenu | Tous les champs, coordonnées des zones, aperçus sur demande. | Synthèse, pages d’origine encadrées, texte retrouvé, empreinte du fichier. |
| Poids | Léger : les aperçus sont retirés par défaut. | Proche du poids des pages analysées ; refusé au-delà de 4 Mo. |
| Décompte | Une analyse par appel réussi. | Identique. |
Solde et usage
GET/api/v1/usage
GET /api/v1/usage renvoie l’état du compte lié à la clé. L’appel ne consomme aucune analyse : il sert à vérifier une clé ou à anticiper un quota. Il compte en revanche dans la limite de débit (voir Quotas).
Exemple d’appel
curl "https://veritas-doc.techstride.app/api/v1/usage" \
-H "Authorization: Bearer vd_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"import requests
API_KEY = "vd_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
response = requests.get(
"https://veritas-doc.techstride.app/api/v1/usage",
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=30,
)
response.raise_for_status()
usage = response.json()
print(usage["plan"], usage["can_analyze"])const API_KEY = "vd_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
const response = await fetch("https://veritas-doc.techstride.app/api/v1/usage", {
headers: { Authorization: `Bearer ${API_KEY}` },
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const usage = await response.json();
console.log(usage.plan, usage.can_analyze);Exemple de réponse
{
"plan": "cabinet",
"credits": 0,
"free_remaining": 0,
"cabinet": {
"used": 12,
"limit": 400,
"period_end": "2026-10-19T00:00:00.000Z"
},
"can_analyze": true
}Champs
| Champ | Type | Description |
|---|---|---|
plan | string | Offre du compte, par exemple cabinet. |
credits | integer | Crédits d’analyse restants. |
free_remaining | integer | Analyses gratuites restantes. |
cabinet | object | null | null sans abonnement Cabinet actif. Sinon : used (documents analysés sur la période), limit (quota de la période) et period_end (fin de période, au format ISO 8601). |
can_analyze | boolean | true si une analyse peut être lancée maintenant. |
Erreurs
Une erreur renvoie un statut HTTP et un corps JSON de cette forme. Appuyez votre logique sur le statut et sur code : le message est un texte lisible, susceptible d’évoluer. Avec format=pdf, les erreurs restent aussi en JSON : testez le statut HTTP avant de traiter le corps comme un PDF.
{
"error": {
"code": "QUOTA_EXHAUSTED",
"message": "Aucune analyse disponible sur ce compte."
}
}| HTTP | Code | Cas | Réessayer ? |
|---|---|---|---|
| 400 | EMPTY_FILEBAD_REQUEST | Corps vide, Content-Type autre que application/pdf ou multipart/form-data, champ file absent, formulaire invalide ou paramètre format invalide (valeurs acceptées : json, pdf). | Non |
| 401 | MISSING_API_KEYINVALID_API_KEY | Clé absente, mal formée, inconnue, révoquée ou expirée. | Non |
| 402 | QUOTA_EXHAUSTED | Aucune analyse disponible sur ce compte. | Non |
| 403 | API_ACCESS_NOT_ALLOWED | Le compte n’a pas accès à l’API. | Non |
| 413 | FILE_TOO_LARGE | Fichier au-delà du plafond de taille configuré sur ce service (aucun plafond par défaut). | Non |
| 413 | REPORT_TOO_LARGE | format=pdf : le relevé annoté dépasse la taille de réponse maximale. Non décompté. Demandez format=json ou réduisez le document. | Non, pas en l’état |
| 415 | NOT_A_PDF | Le contenu ne commence pas par %PDF-. | Non |
| 422 | PDF_ENCRYPTEDPDF_CORRUPTEDNO_PAGES | Le document ne peut pas être analysé (chiffré, corrompu ou sans page). Non décompté. | Non |
| 429 | RATE_LIMITED | Trop de requêtes pour cette clé. L’en-tête Retry-After indique l’attente, en secondes. | Oui, après Retry-After |
| 502 | ENGINE_ERROR | Panne du service d’analyse, sans détail. Non décompté. | Oui, avec délai exponentiel |
| 503 | SERVICE_UNAVAILABLE | Service temporairement indisponible (base de données ou quotas) : aucune analyse n’est lancée. | Oui, avec délai exponentiel |
Quotas et limite de débit
Quota d’analyses
Une analyse lancée par l’API consomme comme une analyse lancée depuis l’interface : d’abord le quota mensuel de l’abonnement Cabinet, puis les analyses gratuites, puis les crédits. Le champ usage.source de la réponse indique ce qui a été utilisé.
Renvoyer le même fichier depuis le même compte dans les 10 minutes est une relance, pas une nouvelle analyse : la réponse porte usage.source: "repeat" et rien n’est décompté. Réessayer dans les 10 minutes qui suivent une coupure réseau ne consomme donc pas de nouvelle analyse ; une relance faite plus tard est décomptée comme une nouvelle analyse. Chaque appel, relance comprise, compte en revanche dans la limite de débit. Consultez GET /api/v1/usage pour connaître votre solde.
Limite de débit
Chaque clé peut envoyer 60 requêtes par minute, sur une fenêtre fixe d’une minute. Au-delà, l’API répond 429 RATE_LIMITED avec l’en-tête Retry-After, en secondes. L’en-tête X-RateLimit-Remaining des réponses de succès vous permet de ralentir avant d’atteindre la limite.
Reprise après erreur
Ne réessayez que les erreurs passagères, avec un délai qui s’allonge.
- Sur
429, attendez au moinsRetry-Aftersecondes avant de réessayer. - Sur
502et503, patientez 1 s, puis 2 s, 4 s, 8 s… en plafonnant le délai (60 s par exemple) et en ajoutant un peu d’aléa, pour que plusieurs clients ne repartent pas tous au même instant. - Limitez le nombre de tentatives (5 par exemple) et journalisez
X-Request-Id. - Ne réessayez pas les autres erreurs
4xx: la même requête donnerait le même résultat.
Exemple de reprise
import random
import time
import requests
API_KEY = "vd_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
ENDPOINT = "https://veritas-doc.techstride.app/api/v1/analyze"
RETRYABLE = {429, 502, 503}
def analyze(path, max_attempts=5):
for attempt in range(max_attempts):
with open(path, "rb") as pdf:
response = requests.post(
ENDPOINT,
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/pdf",
},
data=pdf,
timeout=120,
)
if response.status_code not in RETRYABLE:
break
if attempt == max_attempts - 1:
break
retry_after = response.headers.get("Retry-After", "")
if retry_after.isdigit():
delay = int(retry_after)
else:
delay = min(2 ** attempt, 60) + random.random()
time.sleep(delay)
return response
response = analyze("document.pdf")
print(response.status_code)import { readFile } from "node:fs/promises";
const API_KEY = "vd_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
const ENDPOINT = "https://veritas-doc.techstride.app/api/v1/analyze";
const RETRYABLE = new Set([429, 502, 503]);
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async function analyze(path, maxAttempts = 5) {
const pdf = await readFile(path);
let response;
for (let attempt = 0; attempt < maxAttempts; attempt++) {
response = await fetch(ENDPOINT, {
method: "POST",
headers: {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/pdf",
},
body: pdf,
});
if (!RETRYABLE.has(response.status)) break;
if (attempt === maxAttempts - 1) break;
const retryAfter = Number(response.headers.get("Retry-After"));
const delayMs =
retryAfter > 0
? retryAfter * 1000
: Math.min(2 ** attempt * 1000, 60000) + Math.random() * 1000;
await sleep(delayMs);
}
return response;
}
const response = await analyze("document.pdf");
console.log(response.status);Versionnage et évolutions
- La version est dans le chemin :
/api/v1. Chaque réponse porte l’en-têteX-Veritas-API-Version: 1. - Au sein d’une version, les changements sont additifs uniquement : nouveaux champs, nouveaux endpoints. Votre code doit ignorer les champs qu’il ne connaît pas et tolérer une valeur inattendue dans un champ énuméré (
code,category,type). - Tout changement cassant, comme un champ retiré ou renommé, crée une nouvelle version :
/api/v2. La v1 n’est jamais modifiée de manière cassante. - Cette page est la documentation de référence. Aucun fichier OpenAPI n’est publié.
Bonnes pratiques de sécurité
Une clé d’accès donne le droit d’analyser au nom de votre compte, et donc de consommer votre quota.
Gardez la clé côté serveur
Appelez l’API depuis votre backend uniquement. Ne placez jamais la clé dans une page web, une application mobile, un dépôt de code ou un fichier de configuration versionné. Les appels depuis un navigateur ne sont d’ailleurs pas prévus (aucun en-tête CORS).
Lisez-la depuis l’environnement
Stockez la clé dans une variable d’environnement ou un gestionnaire de secrets (
process.env.VERITAS_API_KEY,os.environ["VERITAS_API_KEY"]) et ne l’écrivez ni dans les journaux ni dans les tickets.Une clé par usage
Un compte peut avoir 5 clés actives. Créez une clé par application ou par environnement (production, recette) et nommez-la clairement : vous pourrez en révoquer une sans interrompre les autres.
Faites tourner les clés
Remplacez vos clés à intervalle régulier : créez la nouvelle clé, déployez-la, vérifiez qu’elle fonctionne, puis révoquez l’ancienne.
Révoquez sans attendre en cas de doute
Depuis Mon compte, la révocation est immédiate : la clé cesse aussitôt de fonctionner. Si une clé a pu fuiter, révoquez-la d’abord, puis créez-en une nouvelle.
Traitez les PDF et les relevés comme des données sensibles
Les documents que vous envoyez peuvent contenir des données personnelles. Ne conservez les PDF et les relevés que le temps nécessaire, et n’envoyez jamais une clé ou un document par e-mail.