> ## Documentation Index
> Fetch the complete documentation index at: https://docs.abbyy.com/llms.txt
> Use this file to discover all available pages before exploring further.

# REST API

Démarrez le conteneur sans argument positionnel : FineParser s'exécute alors comme un serveur HTTP sur le port 8080. Il reste actif tant que vous ne l'arrêtez pas. Utilisez ce mode lorsque vos applications ou d'autres services doivent envoyer des documents à FineParser via le réseau.

```bash theme={null}
docker run -d --name fineparser -p 8080:8080 --stop-timeout 60 \
  -e FINEPARSER_LICENSE_DATA="$(cat acme.fineparserlicense)" \
  -v fineparser-output:/app/output \
  abbyyteam/fineparser
```

Le Recognition mode, les langues et la License sont tous définis au démarrage du conteneur : les requêtes ne contiennent donc ni identifiants ni paramètres de reconnaissance. Les résultats sont écrits dans `/app/output` ; montez-y un volume, sinon ils disparaîtront avec le conteneur. Voir [Configuration](/fr/fine-parser/basics/configuration).

<h2 id="how-a-parse-works">
  Fonctionnement d'une analyse
</h2>

L'analyse est asynchrone : vous soumettez un document et recevez immédiatement un ID de tâche, vous interrogez la tâche jusqu'à ce qu'elle soit terminée, puis vous téléchargez le résultat. La reconnaissance s'effectue sur un seul worker au sein du conteneur, un document à la fois : la connexion HTTP n'est donc jamais maintenue ouverte pendant toute la durée d'une reconnaissance.

<Steps>
  <Step title="Soumettre">
    `POST /parse` avec le document et un type de sortie. La réponse est `202 Accepted`, accompagnée de l'ID de la tâche.
  </Step>

  <Step title="Interroger">
    `GET /jobs/{id}` jusqu'à ce que `status` soit `successful`, `failed` ou `cancelled`.
  </Step>

  <Step title="Télécharger">
    `GET /jobs/{file}`, où `file` est le chemin indiqué par la réponse de statut. Le résultat est renvoyé en flux sous forme de pièce jointe.
  </Step>
</Steps>

<CodeGroup>
  ```bash cURL theme={null}
  # Soumettre
  curl -s -X POST http://localhost:8080/parse \
    -F "file=@document.pdf" \
    -F "outputType=doclang"
  # {"jobId":"6c4f1f0e-...","file":"document.pdf"}

  # Interroger
  curl -s http://localhost:8080/jobs/6c4f1f0e-...
  # {"status":"successful","file":"6c4f1f0e-.../document.pdf.doclang"}

  # Télécharger
  curl -OJ http://localhost:8080/jobs/6c4f1f0e-.../document.pdf.doclang
  ```

  ```python Python theme={null}
  import time
  import requests

  BASE = "http://localhost:8080"

  with open("document.pdf", "rb") as f:
      submitted = requests.post(
          f"{BASE}/parse",
          files={"file": f},
          data={"outputType": "doclang"},
      )
  submitted.raise_for_status()
  job_id = submitted.json()["jobId"]

  while True:
      job = requests.get(f"{BASE}/jobs/{job_id}").json()
      if job["status"] not in ("pending", "in-progress"):
          break
      time.sleep(1)

  if job["status"] != "successful":
      raise RuntimeError(job.get("error", job["status"]))

  result = requests.get(f"{BASE}/jobs/{job['file']}")
  result.raise_for_status()
  with open("document.doclang", "wb") as out:
      out.write(result.content)
  ```

  ```javascript Node.js theme={null}
  import { openAsBlob } from "node:fs";
  import { writeFile } from "node:fs/promises";
  import { setTimeout as sleep } from "node:timers/promises";

  const BASE = "http://localhost:8080";

  const form = new FormData();
  form.append("file", await openAsBlob("document.pdf"), "document.pdf");
  form.append("outputType", "doclang");

  const submitted = await fetch(`${BASE}/parse`, { method: "POST", body: form });
  if (!submitted.ok) throw new Error(await submitted.text());
  const { jobId } = await submitted.json();

  let job;
  do {
    await sleep(1000);
    job = await (await fetch(`${BASE}/jobs/${jobId}`)).json();
  } while (job.status === "pending" || job.status === "in-progress");

  if (job.status !== "successful") throw new Error(job.error ?? job.status);

  const result = await fetch(`${BASE}/jobs/${job.file}`);
  await writeFile("document.doclang", Buffer.from(await result.arrayBuffer()));
  ```
</CodeGroup>

<h2 id="endpoints">
  Points de terminaison
</h2>

Chaque réponse d'erreur est au format JSON et comporte un champ `error` décrivant le problème. Certaines contiennent également un champ `code` ou `status`, comme indiqué ci-dessous.

<h3 id="post-parse">
  POST /parse
</h3>

Soumettez un document. Envoyez-le en `multipart/form-data` avec un champ `file` contenant le document et un champ `outputType` défini sur `doclang`, `json` ou `txt`. Voir [Formats de sortie](/fr/fine-parser/basics/output-formats).

Une soumission réussie renvoie `202 Accepted`, un en-tête `Location` pointant vers la tâche, ainsi qu'un corps contenant l'ID de la tâche et le nom de fichier enregistré par FineParser.

```json theme={null}
{"jobId":"6c4f1f0e-...","file":"document.pdf"}
```

| Statut | Cause                                                                                                                                                                            |
| ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `202`  | Accepté. Interrogez `/jobs/{id}`.                                                                                                                                                |
| `400`  | `outputType` est absent ou ne vaut ni `doclang`, ni `json`, ni `txt`, ou bien la requête ne contient pas exactement une partie nommée `file`.                                    |
| `402`  | Il ne reste aucune page sur votre offre. Le corps contient `code: out_of_credits`. Voir [Atteindre votre limite](/fr/fine-parser/basics/licenses-and-plans#reaching-your-limit). |
| `403`  | Le conteneur n'a pas de License ou celle-ci n'est pas provisionnée. Le corps contient `code: no_instance` ou `code: not_provisioned`.                                            |
| `413`  | Le corps de la requête dépasse la limite de téléversement, soit 1 Gio par défaut. Voir [Configuration](/fr/fine-parser/basics/configuration).                                    |
| `503`  | Le serveur de licences est injoignable ou a demandé une nouvelle tentative. Le corps contient `code: server_unreachable`, `rate_limited` ou `unknown`. Réessayez plus tard.      |

La vérification de la License a lieu avant toute lecture du téléversement : un conteneur sans License rejette donc la requête sans consommer de bande passante ni d'espace disque.

<h3 id="get-jobsid">
  GET /jobs/{id}
</h3>

Renvoie l'état d'une tâche.

```json theme={null}
{"status":"successful","file":"6c4f1f0e-.../document.pdf.doclang"}
```

| `status`      | Signification                                                                                                                              |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `pending`     | En file d’attente, en attente du worker.                                                                                                   |
| `in-progress` | En cours de reconnaissance.                                                                                                                |
| `successful`  | Terminée. `file` est présent tant que le résultat se trouve sur le disque.                                                                 |
| `failed`      | Non terminée. `error` en indique le motif.                                                                                                 |
| `cancelled`   | Le conteneur s’est arrêté pendant la reconnaissance de ce document. État terminal. Soumettez-la à nouveau si vous en avez toujours besoin. |

`file` est le chemin du résultat, au format `{jobId}/{name}`. Faites-le précéder de `/jobs/` pour obtenir l’URL de téléchargement. Le nom correspond au nom du fichier téléversé suivi du type de sortie : ainsi, `document.pdf` analysé en DocLang devient `document.pdf.doclang`. Le champ est absent tant que la tâche n’a pas abouti, et disparaît à nouveau si le résultat est supprimé du disque.

Une tâche peut échouer pour des motifs de licence après avoir été acceptée, car le nombre de pages n’est connu qu’une fois le document chargé. Dans ce cas, `status` vaut `failed` et `error` le précise. Une tâche ayant échoué n’est jamais facturée.

Renvoie `404` si aucune tâche ne correspond.

<h3 id="get-jobsidname">
  GET /jobs/{id}/{name}
</h3>

Téléchargez un résultat. Utilisez la valeur `file` de la réponse de statut comme chemin.

Le résultat est renvoyé sous forme de flux, avec un `Content-Type` correspondant au format de sortie et un en-tête `Content-Disposition` de type attachment indiquant le nom du fichier, de sorte que `curl -OJ` l'enregistre sous le bon nom. Les requêtes par plage ne sont pas prises en charge.

| Statut | Cause                                                                                        |
| ------ | -------------------------------------------------------------------------------------------- |
| `200`  | Le résultat.                                                                                 |
| `404`  | Aucune tâche correspondante, ou le nom ne correspond pas au résultat de cette tâche.         |
| `409`  | La tâche n'a pas abouti. Le corps contient `status`, ainsi que `error` si la tâche a échoué. |
| `410`  | La tâche a abouti, mais son résultat n'est plus présent sur le disque.                       |

<h3 id="delete-jobsid">
  DELETE /jobs/{id}
</h3>

Supprime une tâche et son résultat du disque.

| Statut | Cause                                                                                                 |
| ------ | ----------------------------------------------------------------------------------------------------- |
| `204`  | Supprimée.                                                                                            |
| `404`  | Tâche inexistante.                                                                                    |
| `409`  | La tâche est en cours de reconnaissance et ne peut pas être supprimée. Attendez la fin du traitement. |

<h3 id="get-healthz">
  GET /healthz
</h3>

Indique si le conteneur dispose d'une licence et s'il est prêt à accepter des documents. Utilisez-le comme sonde de disponibilité (readiness probe) dans Kubernetes ou dans votre orchestrateur. L'image de conteneur l'utilise également comme `HEALTHCHECK` Docker.

```bash theme={null}
curl -s localhost:8080/healthz
# {"code":"ok","ready":true,"telemetry":true}
```

Un conteneur prêt renvoie `200`. Un conteneur non prêt renvoie `503` avec `ready: false`, un `code` expliquant la raison et, généralement, un message `detail`. L'état de préparation dépend uniquement de la licence. La télémétrie est communiquée en parallèle et n'a jamais d'incidence sur cet état. Voir [Vérification de la licence](/fr/fine-parser/basics/configuration#confirming-the-license).

<h2 id="results-on-disk">
  Résultats sur le disque
</h2>

Chaque résultat est écrit dans `/app/output` sous la forme `{jobId}/{name}`, à côté d'une petite base de données des tâches. L'organisation des fichiers est stable : vous pouvez donc aussi récupérer les résultats directement depuis le volume monté plutôt que de les télécharger via HTTP.

Par défaut, un résultat est conservé jusqu'à ce que vous exécutiez `DELETE` sur la tâche ou que vous le supprimiez vous-même du volume. Rien n'expire avec le temps. Si vous préférez laisser FineParser faire le ménage, démarrez le conteneur avec `DELETE_ON_DOWNLOAD=true` : chaque résultat est alors supprimé, avec son enregistrement de tâche, dès qu'il a été téléchargé intégralement. Ensuite, le statut comme le téléchargement renvoient `404`. Un téléchargement interrompu laisse le résultat en place pour permettre une nouvelle tentative.

Les fichiers téléversés sont conservés dans un répertoire temporaire distinct et supprimés dès la fin de la reconnaissance, quelle qu'en soit l'issue.

<h2 id="restarts-and-shutdown">
  Redémarrages et arrêt
</h2>

Les tâches sont enregistrées dans la base de données des tâches, située sur le volume de sortie : elles survivent donc à un redémarrage tant que le volume existe. Les tâches en attente sont reprises. Une tâche en cours au moment de l'arrêt du processus est marquée `failed`, car le processus ne peut pas savoir quelle part du résultat a été écrite.

Lors d'un arrêt propre, FineParser cesse d'accepter les requêtes, laisse se terminer celles qui sont en cours, puis se ferme sans attendre le document en cours de reconnaissance. La reconnaissance ne peut pas être interrompue et l'attendre pourrait retarder l'arrêt de plusieurs minutes. La tâche concernée est marquée `cancelled` et son résultat partiel est supprimé. Soumettez-la de nouveau si vous en avez encore besoin.

Attribuez au conteneur un délai d'arrêt de 60 secondes, avec `--stop-timeout 60` sous Docker ou `terminationGracePeriodSeconds: 60` sous Kubernetes. La valeur par défaut de Docker, 10 secondes, écourte la phase de vidage et fait perdre les données de télémétrie d'utilisation accumulées depuis le dernier export.

Un seul conteneur à la fois peut utiliser un répertoire de sortie donné. La base de données des tâches est verrouillée tant qu'elle est ouverte : un second conteneur sur le même volume refusera donc de démarrer. Deux conteneurs nécessitent deux volumes.
