---
seo_title: SDK Kotlin de conversion de fichiers pour la JVM | EnConvert
meta_desc: SDK Kotlin officiel d'EnConvert pour JDK 17+. Data classes idiomatiques pour la conversion de fichiers et pour percevoir, découvrir, distiller et surveiller des pages web.
keywords: sdk kotlin conversion de fichiers, convertir des fichiers en kotlin, url vers pdf en kotlin, api de scraping web kotlin, docx vers pdf en kotlin, enconvert sdk kotlin, client api de conversion jvm, heic vers webp en kotlin, page web vers markdown en kotlin, pipeline d'ingestion rag kotlin, surveiller les changements d'un site en kotlin, bibliothèque de conversion maven central
---

# SDK Kotlin de conversion de fichiers

`com.enconvert:enconvert-kotlin` est le client EnConvert officiel pour Kotlin et la JVM, compilé pour le JDK 17. Il convertit des fichiers sur 43 paires de formats implémentées (DOCX vers PDF, HEIC vers WebP, JSON vers YAML, URL vers PDF, anything to Markdown), et il lit des pages web en direct pour en tirer du Markdown, du JSON, des captures d'écran et du JSONL prêt pour le RAG, le tout prêt à être consommé par un agent, via l'espace de noms `client.v2`. Les options et les réponses sont des data classes Kotlin idiomatiques, avec arguments nommés et valeurs par défaut raisonnables ; le HTTP passe par le `java.net.http.HttpClient` du JDK lui-même, et les conversions lentes se remettent des timeouts de reverse proxy en interrogeant le statut du job.

<div class="alert alert-info">
<strong>Maven Central :</strong> <code>com.enconvert:enconvert-kotlin:0.0.1</code> · <strong>Source :</strong> <a href="https://github.com/conversionapi/kotlin-sdk">conversionapi/kotlin-sdk</a> · <strong>Requiert :</strong> JDK 17+ · <strong>Licence :</strong> MIT
</div>

---

## Installation

```kotlin
// Gradle, DSL Kotlin
dependencies {
    implementation("com.enconvert:enconvert-kotlin:0.0.1")
}
```

```groovy
// Gradle, DSL Groovy
dependencies {
    implementation 'com.enconvert:enconvert-kotlin:0.0.1'
}
```

```xml
<dependency>
  <groupId>com.enconvert</groupId>
  <artifactId>enconvert-kotlin</artifactId>
  <version>0.0.1</version>
</dependency>
```

La seule dépendance d'exécution est `org.jetbrains.kotlinx:kotlinx-serialization-json`. Tout le reste vient du JDK : les requêtes sortent via `java.net.http.HttpClient` et les corps multipart sont assemblés par le SDK lui-même. La toolchain cible la JVM 17, donc n'importe quel runtime JDK 17 ou plus récent convient.

---

## Démarrage rapide

```kotlin
import com.enconvert.Enconvert
import com.enconvert.PerceiveOptions
import com.enconvert.PerceiveOutputName
import com.enconvert.UrlToPdfOptions

fun main() {
    val client = Enconvert(apiKey = System.getenv("ENCONVERT_API_KEY"))

    // Convertit une page en direct en PDF et l'écrit directement sur le disque.
    val pdf = client.convertUrlToPdf("https://example.com", UrlToPdfOptions(saveTo = "page.pdf"))
    println(pdf.presignedUrl)

    // Lit la même page comme votre agent devrait le faire, avec un score de qualité attaché.
    val op = client.v2.perceive(
        "https://example.com",
        PerceiveOptions(outputs = listOf(PerceiveOutputName.MARKDOWN, PerceiveOutputName.STRUCTURED)),
    )
    println("${op.outputs["markdown"]?.url} ${op.renderQuality}") // p. ex. 0.93
}
```

Chaque type EnConvert vit dans le paquet `com.enconvert`, et les extraits ci-dessous omettent les imports ; les types du JDK comme `java.nio.file.Files` et `java.nio.file.Path` apparaissent sans qualification pour la même raison. Chaque méthode est bloquante, puisqu'il n'y a aucune fonction `suspend` : depuis une coroutine, enveloppez donc l'appel dans `withContext(Dispatchers.IO)`. Le client s'authentifie avec une clé API privée envoyée dans l'en-tête `X-API-Key`, ce qui le réserve au côté serveur : ne livrez jamais la clé dans une application Android ou dans quoi que ce soit d'autre que vous distribuez. Voir [Authentification](/fr/docs/authentication) pour les types de clés.

---

## Ce que le client expose

`Enconvert` constitue toute la surface. La conversion de fichiers vit sur le client lui-même ; l'intelligence web vit sur l'espace de noms `v2`, accessible via `client.v2`.

| Surface | Accessible via | Couvre |
|---------|-----------|--------|
| Conversion de fichiers | `client.<method>()` | URL vers PDF, capture d'écran, Markdown ; paires d'images et de documents ; anything-to-PDF et anything-to-Markdown ; lots à l'échelle d'un site ; statut des jobs et des lots |
| Intelligence web | `client.v2.<method>()` | Perceive, discover, lookup, distill, ingest, watch : 23 méthodes sur 21 endpoints REST |

Douze méthodes de conversion correspondent à l'API REST décrite dans la [vue d'ensemble des endpoints](/fr/docs/endpoints-overview) :

| Méthode | Endpoint | Renvoie |
|--------|----------|---------|
| `convertUrlToPdf(url, opts?)` | `POST /v1/convert/url-to-pdf` | `ConversionResult` |
| `convertUrlToScreenshot(url, opts?)` | `POST /v1/convert/url-to-screenshot` | `ConversionResult` |
| `convertUrlToMarkdown(url, opts?)` | `POST /v1/convert/url-to-markdown` | `ConversionResult` |
| `convertImage(file, opts)` | `POST /v1/convert/{from}-to-{to}` | `ConversionResult` |
| `convertDocument(file, opts?)` | `POST /v1/convert/{from}-to-{to}` | `ConversionResult` |
| `convertToMarkdown(file, opts?)` | `POST /v1/convert/anything-to-markdown` | `ConversionResult` |
| `convertToPdf(file, opts?)` | `POST /v1/convert/anything-to-pdf` | `ConversionResult` |
| `convertWebsiteToPdf(url, opts?)` | `POST /v1/convert/website-to-pdf` | `BatchSubmission` |
| `convertWebsiteToScreenshot(url, opts?)` | `POST /v1/convert/website-to-screenshot` | `BatchSubmission` |
| `getJobStatus(jobId)` | `GET /v1/convert/status/{jobId}` | `JobStatus` |
| `getBatchStatus(batchId)` | `GET /v1/convert/batch/{batchId}` | `BatchStatus` |
| `waitForBatch(batchId, opts?)` | `GET /v1/convert/batch/{batchId}` (interrogé en boucle) | `BatchStatus` |

Les quatre méthodes d'envoi de fichier ont chacune quatre surcharges. Le premier argument peut être un chemin `String`, un `java.nio.file.Path`, un simple `ByteArray` (le nom de fichier vaut alors `upload.bin`), ou un `FileInput(data, filename, contentType?)` lorsque vous disposez d'octets bruts et voulez les nommer vous-même.

---

## Conversion de fichiers

### convertUrlToPdf

Effectue le rendu de n'importe quelle URL publique en PDF.

```kotlin
val result = client.convertUrlToPdf(
    "https://example.com/report",
    UrlToPdfOptions(
        render = UrlRenderOptions(viewportWidth = 1440),
        singlePage = false,
        pdfOptions = PdfOptions(pageSize = "A4", orientation = PdfOrientation.LANDSCAPE, margins = PdfMargins(top = 10.0)),
        saveTo = "report.pdf",
    ),
)
println("${result.filename} ${result.fileSize}")
```

| Option | Type | Défaut | Description |
|--------|------|---------|-------------|
| `render` | `UrlRenderOptions` | `UrlRenderOptions()` | Fenêtre d'affichage, médias, défilement, nom de fichier, accès navigateur. |
| `saveTo` | `String?` | -- | Chemin local vers lequel écrire le PDF. Les répertoires parents sont créés pour vous. |
| `singlePage` | `Boolean` | `true` | `true` produit une seule page continue. `false` pagine en utilisant `pdfOptions.pageSize`. |
| `pdfOptions` | `PdfOptions?` | -- | Géométrie de page. Voir [Options PDF](#options-pdf). |

`UrlRenderOptions` est partagé par toutes les conversions basées sur une URL. Il porte `viewportWidth` et `viewportHeight` (1920 x 1080 par défaut), `loadMedia` et `enableScroll` (tous deux à `true` par défaut, ce qui attend les médias et fait défiler la page de haut en bas pour déclencher le chargement paresseux), `outputFilename`, et trois champs d'accès navigateur : `auth` (un `HttpBasicAuth`), `cookies` (une `List<BrowserCookie>`, 50 au maximum) et `headers` (20 au maximum, les en-têtes hop-by-hop sont rejetés).

<div class="alert alert-warning">
<strong>Ne combinez pas <code>auth</code> avec un en-tête <code>Authorization</code>.</strong> L'API rejette le conflit plutôt que de deviner ce que vous vouliez dire.
</div>

### convertUrlToScreenshot

Capture un PNG de n'importe quelle URL. `UrlToScreenshotOptions` ne porte que `render` et `saveTo`.

```kotlin
client.convertUrlToScreenshot(
    "https://example.com",
    UrlToScreenshotOptions(render = UrlRenderOptions(viewportWidth = 1440), saveTo = "shot.png"),
)
```

### convertUrlToMarkdown

Extrait un Markdown GitHub-Flavored propre à partir d'une URL. Le convertisseur supprime la navigation, les pieds de page, les publicités et les scripts, conserve le corps principal de l'article, et ajoute en tête un frontmatter YAML avec le titre, la description, l'url, les liens et les images.

```kotlin
val article = client.convertUrlToMarkdown("https://example.com/post", UrlToMarkdownOptions(saveTo = "article.md"))
println(article.presignedUrl)
```

Lorsque vous voulez aussi un score de qualité, un ensemble d'artefacts ou une extraction structurée issus du même rendu, utilisez plutôt [`client.v2.perceive`](#perceive).

### convertImage

Convertit entre `jpeg`, `png`, `svg`, `heic` et `webp` dans n'importe quel sens, ou rastérise un PDF en JPEG. `ConvertImageOptions` prend un `outputFormat` obligatoire, plus `saveTo` et `outputFilename` facultatifs.

```kotlin
client.convertImage("photo.heic", ConvertImageOptions(outputFormat = "webp", saveTo = "photo.webp"))
client.convertImage(Path.of("logo.svg"), ConvertImageOptions(outputFormat = "png", saveTo = "logo.png"))

val bytes = Files.readAllBytes(Path.of("photo.heic"))
client.convertImage(
    FileInput(data = bytes, filename = "photo.heic"),
    ConvertImageOptions(outputFormat = "webp", saveTo = "photo.webp"),
)
```

Le format d'entrée est résolu à partir de l'extension du nom de fichier. Le format de sortie est normalisé pour vous, `"jpg"` se résout donc en `jpeg`. Les paires non prises en charge lèvent une `EnconvertException` avant tout appel réseau, avec la liste des sorties valides dans le message. Vous pouvez interroger la table de formats directement :

```kotlin
validOutputsFor("pdf")  // [jpeg]
validOutputsFor("json") // [csv, toml, xml, yaml]
validOutputsFor("heic") // [jpeg, png, svg, webp]
```

### convertDocument

Convertit des documents et des formats de données. `outputFormat` vaut `"pdf"` par défaut.

```kotlin
client.convertDocument("report.docx", ConvertDocumentOptions(saveTo = "report.pdf"))
client.convertDocument("data.json", ConvertDocumentOptions(outputFormat = "yaml", saveTo = "data.yaml"))
client.convertDocument(
    "README.md",
    ConvertDocumentOptions(
        outputFormat = "pdf",
        pdfOptions = PdfOptions(pageSize = "A4", margins = PdfMargins(top = 20.0, bottom = 20.0)),
        saveTo = "readme.pdf",
    ),
)
```

**Entrées prises en charge :** `.doc`, `.docx`, `.xls`, `.xlsx`, `.ppt`, `.pptx`, `.html`, `.htm`, `.odt`, `.ods`, `.odp`, `.ots`, `.pages`, `.numbers`, `.md`, `.markdown`, `.csv`, `.json`, `.xml`, `.yaml`, `.yml`, `.toml`.

Les 43 paires implémentées, exactement telles que le SDK les contrôle :

| Entrée | Sorties |
|-------|---------|
| `json` | `csv`, `toml`, `xml`, `yaml` |
| `xml` | `csv`, `json` |
| `csv` | `json`, `xml` |
| `yaml` | `json` |
| `toml` | `json` |
| `markdown` | `html`, `pdf` |
| `html` | `pdf` |
| `doc`, `excel`, `ppt`, `odt`, `ods`, `odp`, `ots`, `pages`, `numbers` | `pdf` |
| `jpeg`, `png`, `svg`, `heic`, `webp` | les uns vers les autres, les 20 paires |
| `pdf` | `jpeg` |

EPUB n'a pas de paire documentaire dédiée. Faites passer les fichiers `.epub` par `convertToPdf` ou `convertToMarkdown`. Les options sont `outputFormat`, `saveTo`, `outputFilename` et `pdfOptions` (pris en compte uniquement lorsque la sortie est un PDF).

### convertToMarkdown

Convertit un fichier envoyé, de presque n'importe quel format documentaire, en Markdown propre. Le format est détecté automatiquement côté serveur : le SDK n'effectue donc aucune vérification d'extension et envoie le fichier tel quel.

```kotlin
client.convertToMarkdown("handbook.docx", ConvertToMarkdownOptions(saveTo = "handbook.md"))
```

**Accepté :** PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT et MD, plus les formats Office hérités et ODF. Les images ne sont pas prises en charge ici.

La sortie est un unique fichier `.md` qui respecte la hiérarchie des titres, ce qui en fait une première étape naturelle pour un pipeline RAG : un découpeur sémantique peut segmenter sur la hiérarchie de titres propre au document plutôt que sur un nombre de caractères arbitraire. Cet endpoint n'accepte aucune option PDF ; `saveTo` et `outputFilename` sont les seules options.

### convertToPdf

Convertit un fichier envoyé, de presque n'importe quel format, en PDF. Les entrées acceptées couvrent Office, ODF, Pages, Numbers, RTF, CSV, HTML, Markdown, texte brut, images matricielles, SVG, EPUB, ainsi qu'un PDF existant en passthrough.

```kotlin
client.convertToPdf("slides.pptx", ConvertToPdfOptions(saveTo = "slides.pdf"))
client.convertToPdf("scan.pdf", ConvertToPdfOptions(pdfOptions = PdfOptions(grayscale = true), saveTo = "gray.pdf"))
```

<div class="alert alert-warning">
<strong>Seul <code>pdfOptions.grayscale</code> est pris en compte sur cet endpoint.</strong> La taille de page, l'orientation, les marges, l'échelle, l'en-tête et le pied de page sont ignorés ici. Lorsque vous avez besoin de la géométrie de page complète, passez plutôt par <code>convertDocument</code> ou <code>convertUrlToPdf</code>.
</div>

### convertWebsiteToPdf et convertWebsiteToScreenshot

Découvrez toutes les pages d'un site, convertissez-les en arrière-plan et récupérez une archive ZIP unique. Les deux méthodes sont asynchrones : elles renvoient immédiatement un `BatchSubmission`, et vous interrogez avec `getBatchStatus` ou bloquez avec `waitForBatch`.

```kotlin
val batch = client.convertWebsiteToPdf(
    "https://example.com",
    WebsiteToPdfOptions(
        website = WebsiteConversionOptions(crawlMode = CrawlMode.SITEMAP, excludePatterns = listOf("/tag/")),
    ),
)
val status = client.waitForBatch(batch.batchId, WaitForBatchOptions(saveTo = "site.zip"))
println("${status.completed} of ${status.total} converted, ${status.failed} failed")
```

`convertWebsiteToScreenshot` fonctionne à l'identique et produit un ZIP de PNG. `WebsiteConversionOptions` porte `render`, `crawlMode` (`AUTO`, `SITEMAP`, `FULL`), `includePatterns`, `excludePatterns`, `notificationEmail` et `callbackUrl`. `waitForBatch` interroge toutes les 5 secondes et abandonne au bout de 30 minutes, deux valeurs remplaçables via `WaitForBatchOptions(intervalMs, timeoutMs, saveTo)` ; en cas de dépassement, il lève une `ApiException` avec le statut `504`.

### getJobStatus

Interroge un job de conversion asynchrone ou récupéré après timeout.

```kotlin
val status = client.getJobStatus("job_abc123")
when (status.status) {
    JobStatusValue.SUCCESS -> println(status.presignedUrl)
    JobStatusValue.FAILED -> System.err.println(status.error)
    JobStatusValue.PROCESSING -> println("still running")
}
```

<div class="alert alert-info">
<strong>Vous avez rarement besoin de l'appeler vous-même.</strong> Le SDK l'interroge déjà lorsqu'une requête synchrone renvoie une 5xx. Voir <a href="#timeout-recovery">Récupération après timeout</a>.
</div>

---

## Intelligence web (V2)

Chaque lecture V2 porte `renderQuality`, un score de 0.0 à 1.0 qui décrit à quel point la page a réellement été rendue proprement. Une page anti-bot, un mur de cookies, une barrière de connexion ou une coquille SPA vide revient avec un score faible, une table `deductions` remplie qui nomme les contrôles déclenchés, et des `warnings`, tandis que le contenu lui-même est quand même renvoyé. Une mauvaise lecture est signalée plutôt que d'entrer discrètement dans le contexte de votre agent. `statusCode` indique le statut HTTP de la réponse finale du document principal, et `contentHash` vous permet de constater que rien n'a changé depuis la lecture précédente. Commencez par la [vue d'ensemble V2](/fr/docs/v2-overview) pour les concepts derrière les six capacités.

| Capacité | Méthodes sur `client.v2` |
|-----------|------------------------|
| Perceive | `perceive`, `getPerceiveOperation`, `perceiveBatch`, `getPerceiveBatch`, `perceiveDirect`, `downloadPerceiveArtifact` |
| Discover | `discover` |
| Lookup | `lookup` |
| Distill | `distill` |
| Ingest | `ingest`, `ingestFiles`, `listIngestJobs`, `getIngestJob`, `cancelIngestJob`, `retryIngestWebhook`, `getWebhookSecret`, `rotateWebhookSecret` |
| Watch | `createWatcher`, `listWatchers`, `getWatcher`, `getWatcherSnapshots`, `updateWatcher`, `deleteWatcher` |

### Perceive

Effectue le rendu d'une URL dans les artefacts que vous demandez. Synchrone : l'appel renvoie une opération terminée dont les URL d'artefacts sont signées pour 15 minutes. Référence complète dans [Perceive](/fr/docs/v2-perceive).

```kotlin
val op = client.v2.perceive(
    "https://example.com/pricing",
    PerceiveOptions(
        outputs = listOf(PerceiveOutputName.MARKDOWN, PerceiveOutputName.SCREENSHOT_FULL_PAGE, PerceiveOutputName.STRUCTURED),
        extract = listOf(PerceiveExtractName.TABLES, PerceiveExtractName.METADATA),
        viewport = PerceiveViewport(width = 1440),
        waitFor = "css:.pricing-table",
    ),
)

if ((op.renderQuality ?: 0.0) < 0.5) System.err.println("Low quality read: ${op.deductions} ${op.warnings}")
println(op.outputs["markdown"]?.url)
println(op.structured)
```

| Option | Type | Défaut | Description |
|--------|------|---------|-------------|
| `outputs` | `List<PerceiveOutputName>?` | `[MARKDOWN, STRUCTURED]` | `MARKDOWN`, `HTML_CLEANED`, `HTML_RAW`, `SCREENSHOT`, `SCREENSHOT_FULL_PAGE`, `PDF`, `LINKS`, `IMAGES`, `STRUCTURED`. |
| `extract` | `List<PerceiveExtractName>?` | -- | `TABLES`, `PRICES`, `CONTACTS`, `METADATA`, `MAIN_CONTENT`, `HEADINGS`, `STRUCTURED_DATA`, `TECHNOLOGIES`, `ALL`. |
| `schema` | `Map<String, Any?>?` | -- | Schéma JSON pour l'extraction structurée. |
| `waitFor` / `waitTimeoutMs` | `String?` / `Int?` | -- / `30000` | Un sélecteur CSS (éventuellement préfixé par `css:`) ou `js:<expr>` à attendre, et son budget, de 0 à 60000. |
| `jsCode` | `String?` | -- | JavaScript exécuté après la navigation, 20000 caractères maximum. |
| `viewport` | `PerceiveViewport?` | 1920 x 1080 | `width` de 320 à 3840, `height` de 240 à 2160. |
| `headers` / `cookies` / `auth` | -- | -- | En-têtes supplémentaires, cookies injectés, identifiants HTTP Basic. |
| `cacheMode` | `PerceiveCacheMode?` | `ENABLED` | `ENABLED` (cache d'1 heure), `BYPASS`, `REFRESH`. |
| `pdfOptions` | `PdfOptions?` | -- | N'a de sens que lorsque `outputs` inclut `PDF`. |
| `blockResources` | `List<PerceiveResourceType>?` | -- | Types de ressources que le navigateur ne doit pas charger. |
| `respectRobots` / `mobile` | `Boolean?` | -- | Respecter `robots.txt` ; effectuer le rendu avec un profil mobile. |
| `onlyMainContent` | `Boolean?` | `true` | Supprime la navigation, l'en-tête, le pied de page et les bandeaux de cookies de l'artefact Markdown et de l'extrait `main_content`. |
| `directDownload` | `Boolean?` | -- | Renvoie les octets de l'artefact au lieu d'une enveloppe JSON. Préférez `perceiveDirect`. |

`proxyUrl`, `geolocation` et `actionChain` existent sur `PerceiveOptions` mais ne sont pas encore disponibles côté serveur : ils sont actuellement rejetés avec un `422`.

Traitez jusqu'à 1000 URL par lot derrière un bloc d'options partagé. Les petits lots se terminent en ligne ; les plus gros reviennent en `QUEUED`, interrogez donc l'identifiant de job. `getPerceiveOperation` resigne les URL d'artefacts de n'importe quelle opération antérieure.

```kotlin
val batch = client.v2.perceiveBatch(
    listOf("https://a.example.com", "https://b.example.com"),
    PerceiveBatchOptions(
        options = PerceiveOptions(outputs = listOf(PerceiveOutputName.MARKDOWN)),
        outputMode = PerceiveBatchOutputMode.ZIP,
    ),
)

var job = client.v2.getPerceiveBatch(batch.jobId)
while (job.status == PerceiveBatchStatus.QUEUED || job.status == PerceiveBatchStatus.PROCESSING) {
    Thread.sleep(5_000)
    job = client.v2.getPerceiveBatch(batch.jobId)
}
println("${job.completed}/${job.total} done, zip at ${job.zip?.url}")

val again = client.v2.getPerceiveOperation(op.operationId) // URL fraîchement signées
```

Lorsque vous ne voulez que les octets, `perceiveDirect` renvoie l'artefact sur la même requête et évite l'aller-retour par URL signée. Il exige exactement une sortie produisant un artefact, c'est-à-dire n'importe quoi sauf `STRUCTURED`, et lève une `EnconvertException` en local si vous en demandez zéro ou plus d'une.

```kotlin
val direct = client.v2.perceiveDirect("https://example.com", PerceiveOptions(outputs = listOf(PerceiveOutputName.PDF)))
Files.write(Path.of(direct.filename ?: "page.pdf"), direct.content)
println("${direct.renderQuality} ${direct.sourceStatusCode} ${direct.warningsCount}")

// Retélécharge un artefact stocké d'une opération antérieure.
val markdown = client.v2.downloadPerceiveArtifact(op.operationId, PerceiveOutputName.MARKDOWN)
println(markdown.content.toString(Charsets.UTF_8))
```

`downloadPerceiveArtifact` accepte un `output` nul lorsque l'opération n'a produit qu'un seul artefact, et renvoie `410` dès que l'artefact stocké dépasse sa fenêtre de rétention.

### Discover

Énumère les URL d'un site sans aucun rendu navigateur. Référence complète dans [Discover](/fr/docs/v2-discover).

```kotlin
val found = client.v2.discover(
    "https://example.com",
    DiscoverOptions(mode = DiscoverMode.HYBRID, maxUrls = 200, maxDepth = 3, excludePatterns = listOf("/tag/")),
)
println("${found.total} urls, truncated=${found.truncated}, sources=${found.sources}")
```

| Option | Type | Défaut | Description |
|--------|------|---------|-------------|
| `mode` | `DiscoverMode?` | `HYBRID` | `SITEMAP`, `CRAWL` ou `HYBRID` (sitemap plus exploration HTTP). |
| `maxUrls` / `maxDepth` | `Int?` | `100` / `2` | 1-1000 et 1-5. |
| `includePatterns` / `excludePatterns` | `List<String>?` | -- | Liste d'autorisation et liste d'exclusion en expressions régulières, 50 entrées maximum chacune. La liste d'exclusion est appliquée en second. |
| `sameDomainOnly` | `Boolean?` | `true` | Rester sur le domaine de départ. |
| `respectRobots` | `Boolean?` | -- | Respecter `robots.txt`. |

`DiscoverResult.sources` indique les comptages bruts par source avant déduplication, par exemple `{sitemap=42, crawl=30}`.

### Lookup

Lance une recherche web catégorisée et, si vous le souhaitez, perçoit les premiers résultats dans le même appel. Référence complète dans [Lookup](/fr/docs/v2-lookup).

```kotlin
val search = client.v2.lookup(
    "best static site generators",
    LookupOptions(category = LookupCategory.WEB, numResults = 10, country = "us", timeFilter = LookupTimeFilter.MONTH, perceiveTop = 3),
)

for (hit in search.results) {
    println("${hit.position}. ${hit.title} ${hit.url}")
    hit.perceive?.let { println("   rendered at quality ${it.renderQuality}") }
}
```

| Option | Type | Défaut | Description |
|--------|------|---------|-------------|
| `category` | `LookupCategory?` | `WEB` | `WEB`, `NEWS`, `IMAGES`, `SCHOLAR`, `PATENTS`, `MAPS`. |
| `country` / `locale` | `String?` | -- | Code pays Google `gl` et langue d'interface `hl`. |
| `timeFilter` | `LookupTimeFilter?` | -- | `HOUR`, `DAY`, `WEEK`, `MONTH`, `YEAR`. |
| `numResults` / `page` | `Int?` | `10` / `1` | 1-100 et 1-10. |
| `location` | `String?` | -- | Localisation en texte libre, par exemple `"Austin, Texas"`. |
| `autocorrect` | `Boolean?` | `true` | Laisser le fournisseur corriger les fautes de frappe. |
| `perceiveTop` | `Int?` | `0` | Percevoir automatiquement les N premières URL de résultat, 0-10. Chacune déclenche un rendu navigateur complet. |

Le résultat porte aussi `answerBox`, `knowledgeGraph`, `perceiveOperationIds` et `total`.

### Distill

Pointez un schéma vers quelques pages et récupérez des données structurées. Une passe CSS optionnelle répond à tout ce qu'elle peut avant toute escalade vers le niveau LLM. Référence complète dans [Distill](/fr/docs/v2-distill).

```kotlin
val extraction = client.v2.distill(
    DistillOptions(
        urls = listOf("https://example.com/pricing"),
        schema = mapOf("plans" to "list of plan names with monthly prices"),
        cssSchema = CssSchema(
            baseSelector = ".plan-card",
            fields = listOf(
                CssField(name = "name", type = CssFieldType.TEXT, selector = "h3"),
                CssField(name = "price", type = CssFieldType.TEXT, selector = ".price"),
            ),
            targetField = "plans",
        ),
    ),
)

for (item in extraction.results) {
    println("${item.url} tier=${item.extractionTier} css=${item.fieldsFromCss} llm=${item.fieldsFromLlm}")
    println(item.data)
}
```

Ou découvrez d'abord les URL et distillez-les une à une :

```kotlin
client.v2.distill(
    DistillOptions(
        discoverFrom = DistillDiscoverFrom(url = "https://example.com", mode = DiscoverMode.SITEMAP, maxPages = 10),
        schema = mapOf("title" to "page title", "summary" to "one-line summary"),
    ),
)
```

Fournissez exactement l'un des deux : `urls` ou `discoverFrom`. Passer les deux, ou aucun, lève une `EnconvertException` avant que la requête ne quitte votre processus.

| Option | Type | Défaut | Description |
|--------|------|---------|-------------|
| `urls` | `List<String>?` | -- | URL explicites à distiller, 50 au maximum. |
| `discoverFrom` | `DistillDiscoverFrom?` | -- | Découvrir d'abord les URL d'un site. `maxPages` va de 1 à 50, avec 10 par défaut. |
| `schema` | `Map<String, Any?>` | obligatoire | Un objet JSON Schema, ou une table plate `{field to description}`. |
| `cssSchema` | `CssSchema?` | -- | Passe CSS exécutée avant toute escalade LLM. |
| `waitFor` / `waitTimeoutMs` | `String?` / `Int?` | -- / `30000` | Sélecteur ou expression `js:` à attendre, et son budget. |
| `headers` / `cookies` / `respectRobots` | -- | -- | Mêmes contrôles de rendu que perceive. |

`CssField.type` vaut l'un de `TEXT`, `ATTRIBUTE`, `HTML`, `REGEX`, `NESTED`, `LIST`, `NESTED_LIST`. `ATTRIBUTE` exige `attribute`, `REGEX` exige `pattern`, et les trois variantes imbriquées exigent une liste `fields` non vide, jusqu'à cinq niveaux de profondeur.

### Ingest

Transforme un site entier, ou une pile de documents envoyés, en JSONL découpé et prêt pour le RAG à travers un seul pipeline. Ingest est toujours asynchrone. Référence complète dans [Ingest](/fr/docs/v2-ingest).

```kotlin
val job = client.v2.ingest(
    IngestOptions(
        mode = IngestMode.SITEMAP,
        url = "https://docs.example.com",
        maxPages = 100,
        chunk = IngestChunkOptions(maxWords = 512, sentenceOverlap = 1),
        webhookUrl = "https://my.app/hooks/enconvert",
    ),
)

var state = client.v2.getIngestJob(job.jobId)
while (state.status !in setOf(IngestStatus.COMPLETED, IngestStatus.FAILED, IngestStatus.CANCELED)) {
    Thread.sleep(10_000)
    state = client.v2.getIngestJob(job.jobId)
}
if (state.status == IngestStatus.COMPLETED) println("${state.totalChunks} chunks at ${state.outputUrl}")
```

`mode` vaut `URLS` par défaut, ce qui exige une liste `urls` non vide et rejette `url`. `SITEMAP` et `CRAWL` exigent une `url` de départ et rejettent `urls`. Le SDK applique les deux règles en local et lève une `EnconvertException` plutôt que d'envoyer une requête qui ne peut pas aboutir.

Les fichiers envoyés passent par `ingestFiles`, qui partage le même cycle de vie de job sous le mode `FILES` et accepte PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT et MD, ainsi que les documents Office hérités et ODF.

```kotlin
val paths = listOf(Path.of("handbook.pdf"), Path.of("notes.docx"))
val fileJob = client.v2.ingestFiles(
    paths.map { FileInput(data = Files.readAllBytes(it), filename = it.fileName.toString()) },
    IngestFilesOptions(chunk = IngestChunkOptions(maxWords = 512, sentenceOverlap = 1)),
)
println(fileJob.jobId)
```

| Option | Type | Défaut | Description |
|--------|------|---------|-------------|
| `mode` | `IngestMode?` | `URLS` | `URLS`, `SITEMAP`, `CRAWL`, `FILES`. |
| `url` / `urls` | `String?` / `List<String>?` | -- | URL de départ pour `SITEMAP` et `CRAWL` ; URL explicites (1000 au maximum) pour `URLS`. |
| `maxPages` / `maxDepth` | `Int?` | `50` / `2` | Plafonds de découverte, 1-1000 et 1-5. |
| `sameDomainOnly` | `Boolean?` | `true` | Rester sur le domaine de départ. |
| `includePatterns` / `excludePatterns` / `respectRobots` | -- | -- | Mêmes contrôles de découverte que `discover`. |
| `waitFor` / `waitTimeoutMs` | `String?` / `Int?` | -- / `30000` | Attente de rendu par page. |
| `chunk` | `IngestChunkOptions?` | -- | `maxWords` de 32 à 4000 (512 par défaut), `sentenceOverlap` de 0 à 10 (1 par défaut). |
| `webhookUrl` | `String?` | -- | Webhook de fin, signé en HMAC. |

Gestion des jobs et plomberie des webhooks :

```kotlin
client.v2.listIngestJobs(V2ListOptions(limit = 20))   // du plus récent au plus ancien
client.v2.cancelIngestJob(job.jobId)                  // idempotent

val secret = client.v2.getWebhookSecret()
println("${secret.signatureHeader} ${secret.signatureScheme} ${secret.replayToleranceSeconds}s")

client.v2.rotateWebhookSecret()         // les anciennes signatures cessent immédiatement d'être valides
client.v2.retryIngestWebhook(job.jobId) // renvoie le webhook d'un job terminé
```

### Watch

Refait le rendu d'une URL à intervalle fixe et vous notifie quand elle change. Référence complète dans [Watch](/fr/docs/v2-watch).

```kotlin
val watcher = client.v2.createWatcher(
    "https://example.com/pricing",
    WatchCreateOptions(
        frequencyMinutes = 60,
        diffMode = WatchDiffMode.AUTO,
        webhookUrl = "https://my.app/hooks/changes",
        notifyEmail = true,
    ),
)

client.v2.listWatchers(V2ListOptions(limit = 20))
for (snap in client.v2.getWatcherSnapshots(watcher.watcherId, SnapshotListOptions(limit = 10)).snapshots) {
    println("${snap.checkedAt} changed=${snap.hasChanges} similarity=${snap.similarity}")
}

client.v2.updateWatcher(watcher.watcherId, WatcherUpdate(status = WatcherUpdateStatus.PAUSED))
client.v2.updateWatcher(watcher.watcherId, WatcherUpdate(webhookUrl = "")) // efface le webhook
client.v2.deleteWatcher(watcher.watcherId)                                 // suppression logique, idempotente
```

| Option | Type | Défaut | Description |
|--------|------|---------|-------------|
| `frequencyMinutes` | `Int?` | `60` | Minutes entre deux vérifications, 60-43200. Le plancher horaire est strict. |
| `diffMode` | `WatchDiffMode?` | `AUTO` | `AUTO`, `TEXT`, `STRUCTURED`, `TABLES`, `METADATA`. |
| `trackFields` | `Map<String, Any?>?` | -- | Sous-ensemble de champs ou de sélecteurs que le moteur de diff doit surveiller. |
| `webhookUrl` | `String?` | -- | Webhook de changement, signé en HMAC avec le même secret qu'ingest. |
| `notifyEmail` | `Boolean?` | `true` | Notifier le propriétaire du projet par e-mail en cas de changement. |

`updateWatcher` exige au moins un champ et lève une `EnconvertException` sur un `WatcherUpdate` vide. Son `status` n'accepte que `ACTIVE` ou `PAUSED` ; la suppression passe par `deleteWatcher`, qui renvoie le watcher marqué supprimé avec le statut `DELETED`. `getWatcher` sur un watcher supprimé répond `404`.

<div class="alert alert-warning">
<strong>Les diffs de snapshots contiennent du contenu de page non fiable.</strong> <code>WatcherSnapshot.changes</code> est du texte brut extrait de la page surveillée. Échappez-le avant de l'afficher dans du HTML, un tableau de bord ou un message de chat.
</div>

---

## Options PDF

`PdfOptions` est partagé par `convertUrlToPdf`, `convertDocument`, `convertToPdf` (niveaux de gris uniquement), `convertWebsiteToPdf` et `PerceiveOptions.pdfOptions`.

```kotlin
client.convertUrlToPdf(
    "https://example.com",
    UrlToPdfOptions(
        pdfOptions = PdfOptions(
            pageSize = "A4",
            orientation = PdfOrientation.LANDSCAPE,
            margins = PdfMargins(top = 10.0, bottom = 10.0, left = 15.0, right = 15.0),
            scale = 0.9,
            header = PdfHeaderFooter(content = "Quarterly report", height = 12.0),
        ),
        saveTo = "report.pdf",
    ),
)
```

| Champ | Type | Description |
|-------|------|-------------|
| `pageSize` | `String?` | `"A4"`, `"A3"`, `"Letter"`, `"Legal"`, et ainsi de suite. |
| `pageWidth` / `pageHeight` | `Double?` | Géométrie personnalisée. Ensemble, elles remplacent `pageSize`. |
| `orientation` | `PdfOrientation?` | `PORTRAIT` ou `LANDSCAPE`. Portrait par défaut. |
| `margins` | `PdfMargins?` | `top`, `bottom`, `left`, `right`, tous des doubles facultatifs, en mm. |
| `scale` | `Double?` | Échelle de rendu, par exemple `0.9` pour 90 pour cent. |
| `grayscale` | `Boolean?` | Post-traite le PDF en niveaux de gris. |
| `header` / `footer` | `PdfHeaderFooter?` | `content` (2000 caractères maximum) et `height`. |

Seuls les champs que vous définissez réellement sont sérialisés sur le réseau : un `PdfOptions` partiellement rempli ne remplace donc jamais une valeur par défaut du serveur à laquelle vous n'avez pas touché. La matrice complète des paramètres se trouve dans [Paramètres et options](/fr/docs/parameters-options).

---

## Gestion des erreurs

Chaque échec est une `EnconvertException` ou une de ses sous-classes : un seul `catch` peut donc servir de filet de sécurité pendant que des sous-classes précises traitent les cas qui vous intéressent.

```kotlin
try {
    client.v2.perceive("https://example.com")
} catch (e: AuthenticationException) {
    System.err.println("Invalid or missing API key")
} catch (e: QuotaException) {
    System.err.println("Request rejected with 402")
} catch (e: RateLimitException) {
    System.err.println("Too many requests, back off and retry")
} catch (e: ApiException) {
    System.err.println("API error [${e.statusCode}]: ${e.message}")
} catch (e: EnconvertException) {
    System.err.println("Client-side validation failed: ${e.message}")
}
```

| Classe | Levée sur | Code de statut |
|-------|-----------|-------------|
| `AuthenticationException` | Clé API invalide, absente ou révoquée | `401`, `403` |
| `QuotaException` | Levée sur un HTTP 402 | `402` |
| `RateLimitException` | Trop de requêtes | `429` |
| `ApiException` | Toute autre réponse 4xx ou 5xx | le code réel |
| `EnconvertException` | Classe de base, plus la validation côté client comme une paire de conversion non prise en charge ou un objet d'options mal formé | -- |

`ApiException` expose la propriété brute `statusCode`, et son `message` s'affiche sous la forme `[<statusCode>] <server message>`, avec le champ `detail` ou `error` du serveur extrait du corps JSON. L'ordre des `catch` compte : les trois classes précises étendent toutes `ApiException`, qui étend `EnconvertException`, listez-les donc en premier. La table des messages est documentée dans [Codes d'erreur](/fr/docs/error-codes).

---

## Récupération après timeout

Les rendus URL vers PDF longs et les conversions de gros documents peuvent dépasser un timeout de reverse proxy de 60 à 120 secondes même lorsque la conversion réussit côté serveur. Le SDK gère cela sur les méthodes de conversion V1 :

1. Avant chaque requête, il génère un identifiant de job hexadécimal de 32 caractères et l'envoie comme `job_id` dans le corps JSON ou comme champ multipart.
2. Si la requête revient en 5xx, le SDK cesse de faire confiance à la réponse et interroge `GET /v1/convert/status/{job_id}` toutes les 3 secondes. Un `404` pendant que la ligne du job est encore en cours d'écriture signifie « continuez d'attendre ».
3. Sur `success`, le SDK transforme la charge utile en un `ConversionResult` normal. Sur `failed`, il lève une `ApiException` portant le message d'erreur du serveur.
4. Le délai est de 5 minutes, après quoi il lève `ApiException(504, "Conversion timed out")`.

Les réponses réussies qui omettent `job_id` (c'est le cas du chemin URL synchrone) reçoivent l'identifiant généré par le client : `result.jobId` est donc toujours quelque chose que vous pouvez passer à `getJobStatus`. Deux exceptions délibérées : `convertWebsiteToPdf` et `convertWebsiteToScreenshot` sautent ce repli, car une soumission de site n'a pas de ligne de job par unité et une 5xx y signifie que la soumission elle-même a échoué. Les méthodes V2 le sautent aussi, puisque chaque endpoint V2 dispose de son propre mécanisme d'interrogation ou de webhook.

---

## Configuration

```kotlin
val client = Enconvert(
    apiKey = System.getenv("ENCONVERT_API_KEY"),
    timeout = 300_000,                      // ms, 5 minutes
    baseUrl = "https://api.enconvert.com",  // à remplacer pour une passerelle auto-hébergée
)
```

| Paramètre | Type | Défaut | Description |
|-----------|------|---------|-------------|
| `apiKey` | `String` | obligatoire | Clé API privée. Une valeur vide fait lever une `IllegalArgumentException` par le constructeur. |
| `timeout` | `Long` | `300_000` | Timeout par requête, en millisecondes, appliqué au `HttpRequest` sous-jacent. |
| `baseUrl` | `String` | `https://api.enconvert.com` | URL de base de l'API. Les barres obliques finales sont supprimées. |

<div class="alert alert-warning">
<strong>Ne codez jamais la clé API en dur.</strong> Lisez-la depuis une variable d'environnement, une propriété Gradle ou votre gestionnaire de secrets, et gardez-la hors de tout artefact livré sur l'appareil d'un utilisateur. Quiconque détient votre clé privée peut lancer des requêtes sur votre projet.
</div>

---

## Forme du résultat

Chaque méthode de conversion renvoie un `ConversionResult` :

```kotlin
public data class ConversionResult(
    val presignedUrl: String,
    val objectKey: String,
    val filename: String,
    val fileSize: Long? = null,
    val conversionTimeSeconds: Double? = null,
    val jobId: String? = null,
)
```

L'URL présignée est un lien signé temporaire. Passez `saveTo` si vous voulez les octets sur le disque immédiatement, ou téléchargez l'URL vous-même et rangez le fichier dans votre propre bucket pour un accès permanent.

Les lectures V2 renvoient plutôt un `PerceiveResult`, où se trouvent les signaux de fiabilité :

```kotlin
val op = client.v2.perceive("https://example.com")

op.operationId    // "per_..."
op.status         // PerceiveStatus.COMPLETED
op.url            // URL demandée ; op.urlFinal après les redirections
op.renderQuality  // Double?, de 0.0 à 1.0
op.statusCode     // Int?, statut HTTP du document principal
op.deductions     // Map<String, Double>, p. ex. {http_error=0.7}. Vide sur un rendu propre.
op.warnings       // List<String>
op.cacheHit       // Boolean ; op.contentHash est le SHA-256 du contenu rendu
op.outputs        // Map<String, V2OutputArtifact> indexée par nom de sortie
op.structured     // Map<String, Any?>?, présent quand extract ou schema a été utilisé
op.extractionTier // HEURISTIC, CSS ou LLM
op.tokens         // V2Tokens(input, output) ; op.costCents et op.durationMs à côté
```

Chaque `V2OutputArtifact` porte `url`, `objectKey`, `sizeBytes`, `contentType` et `expiresIn` (900 secondes). Les URL d'artefacts sont resignées à chaque appel de `getPerceiveOperation` : stockez donc l'`operationId`, pas l'URL. Les charges utiles non typées (schémas d'extraction, données extraites, champs suivis, entrées de diff, extras de recherche) traversent la frontière sous forme de `Map<String, Any?>` et se convertissent sans perte dans les deux sens : rien de ce que vous mettez dans un schéma n'est remodelé en sortie.

---

## Sources et signalements

- **Maven Central :** `com.enconvert:enconvert-kotlin:0.0.1`
- **GitHub :** [conversionapi/kotlin-sdk](https://github.com/conversionapi/kotlin-sdk)
- **Licence :** MIT
- **Autres langages :** voir la [liste complète des SDK](/fr/docs/sdks)

---

## Questions fréquentes

### Comment convertir des fichiers en Kotlin ?

Ajoutez `com.enconvert:enconvert-kotlin:0.0.1` à votre build Gradle ou Maven, construisez `Enconvert(apiKey = System.getenv("ENCONVERT_API_KEY"))`, et appelez une méthode typée comme `convertDocument`, `convertImage` ou `convertUrlToPdf`. Passez `saveTo` dans l'objet d'options pour écrire la sortie directement dans un fichier local plutôt que de télécharger vous-même l'URL présignée.

### Comment convertir un DOCX en PDF en Kotlin ?

Appelez `client.convertDocument("report.docx", ConvertDocumentOptions(saveTo = "report.pdf"))`. Le format de sortie vaut `pdf` par défaut, vous pouvez donc laisser `outputFormat` non défini. Le format d'entrée est résolu à partir de l'extension du fichier, et `.doc` comme `.docx` pointent vers la même conversion. Pour la géométrie de page, passez un `PdfOptions` via `ConvertDocumentOptions.pdfOptions`.

### Comment convertir une URL en PDF en Kotlin ?

Appelez `client.convertUrlToPdf(url, UrlToPdfOptions(saveTo = "page.pdf"))`. Mettez `singlePage = false` pour paginer avec `pdfOptions.pageSize`, et utilisez `UrlRenderOptions` pour changer la fenêtre d'affichage, désactiver le chargement des médias ou couper la passe de défilement qui déclenche le chargement paresseux.

### Comment convertir du HEIC en WebP sur la JVM ?

Appelez `client.convertImage("photo.heic", ConvertImageOptions(outputFormat = "webp", saveTo = "photo.webp"))`. Les 20 paires entre `jpeg`, `png`, `svg`, `heic` et `webp` sont implémentées, plus la rastérisation `pdf` vers `jpeg`. Une paire non prise en charge lève une `EnconvertException` avant tout appel réseau, et `validOutputsFor("heic")` liste les cibles valides en amont.

### Comment récupérer une page web en Markdown depuis Kotlin ?

Deux possibilités. `client.convertUrlToMarkdown(url, UrlToMarkdownOptions(saveTo = "page.md"))` vous donne un fichier Markdown avec un frontmatter YAML. `client.v2.perceive(url, PerceiveOptions(outputs = listOf(PerceiveOutputName.MARKDOWN)))` vous donne le même contenu plus `renderQuality`, `deductions`, `warnings` et `statusCode`, ce qui est exactement ce qu'il vous faut lorsqu'un agent va lire le résultat sans supervision.

### Que signifie renderQuality et quand faut-il rejeter une page ?

`renderQuality` va de 0.0 à 1.0 et décrit à quel point la page a été rendue proprement, pas la qualité du contenu. Les pages anti-bot, les murs de connexion, les erreurs HTTP et les coquilles SPA vides le font baisser, et `deductions` nomme chaque contrôle déclenché, par exemple `{http_error=0.7}`. Le contenu est toujours renvoyé pour que vous puissiez l'inspecter. Un schéma courant consiste à considérer tout ce qui est en dessous de 0.5 comme suspect, puis à redemander la page avec `cacheMode = PerceiveCacheMode.REFRESH` ou à la router vers un humain.

### Comment transformer un site de documentation en chunks RAG depuis Kotlin ?

Appelez `client.v2.ingest(IngestOptions(mode = IngestMode.SITEMAP, url = "https://docs.example.com", chunk = IngestChunkOptions(maxWords = 512, sentenceOverlap = 1)))`. Ingest est toujours asynchrone : interrogez `getIngestJob(jobId)` jusqu'à ce que le statut soit `COMPLETED` et lisez `outputUrl` pour récupérer le JSONL, ou définissez `webhookUrl` et laissez le webhook de fin vous trouver. Les documents locaux passent par `ingestFiles` avec les mêmes réglages de découpage.

### Le SDK bloque-t-il le thread appelant ?

Oui. Chaque méthode appelle `HttpClient.send` de manière synchrone, et il n'y a ni fonction `suspend` ni builder de coroutine dans le SDK. `waitForBatch` et l'interrogateur interne de récupération après timeout endorment le thread courant entre deux tentatives. Depuis une coroutine, enveloppez les appels dans `withContext(Dispatchers.IO)` ; depuis un framework serveur, gardez-les hors du pool de threads qui traite les requêtes.

### Puis-je appeler ce SDK depuis Java ?

Oui, puisque ce sont des classes JVM ordinaires, mais les arguments par défaut de Kotlin ne sont pas exposés à Java sous forme de surcharges : un appelant Java doit donc passer tous les arguments de constructeur d'une data class d'options. Si votre base de code est en Java, utilisez plutôt le SDK Java distinct listé sur la [page des SDK](/fr/docs/sdks).

### Où obtenir une clé API ?

Créez une clé privée dans votre [tableau de bord](/fr/dashboard). Elle est envoyée dans l'en-tête `X-API-Key` à chaque requête, gardez-la donc côté serveur. Les types de clés et leurs portées sont traités dans [Authentification](/fr/docs/authentication), et la [tarification](/fr/pricing) couvre le volet commercial.
