---
seo_title: SDK Ruby de conversion de fichiers (RubyGems) | EnConvert
meta_desc: SDK Ruby officiel EnConvert pour Ruby 3.0+. Installez la gem enconvert pour convertir des fichiers et pour perceive, discover, distill, ingest et watch depuis Ruby.
keywords: sdk ruby conversion de fichiers, convertir des fichiers en ruby, url vers pdf ruby, api scraping web ruby, docx vers pdf ruby, enconvert ruby sdk, client api conversion rubygems, heic vers webp ruby, html vers pdf gem ruby, page web vers markdown ruby, extraction de données structurées ruby, gem crawler de site ruby
---

# SDK Ruby de conversion de fichiers

`enconvert` est la gem Ruby officielle de l'API EnConvert. Elle convertit des URL, des images et des documents à travers 43 endpoints de conversion implémentés, et expose un second espace de noms, `client.v2`, qui lit des pages web en direct sous forme de Markdown, de JSON, de captures d'écran et de PDF prêts pour un agent. Chaque lecture V2 porte un score `render_quality`, si bien qu'une page bloquée ou une coquille SPA vide ne passe jamais pour du vrai contenu. La gem cible Ruby 3.0+ et n'a aucune dépendance à l'exécution : elle s'appuie sur `net/http`, `json` et `securerandom` de la bibliothèque standard. Les réponses reviennent sous forme de Structs avec des accesseurs en snake_case.

<div class="alert alert-info">
<strong>RubyGems :</strong> <code>enconvert</code> · <strong>Source :</strong> <a href="https://github.com/conversionapi/ruby-sdk">conversionapi/ruby-sdk</a> · <strong>Ruby :</strong> 3.0.0 ou plus récent · <strong>Dépendances à l'exécution :</strong> aucune
</div>

---

## Installation

```bash
gem install enconvert
```

Ou ajoutez-la à votre `Gemfile` :

```ruby
gem "enconvert"
```

```bash
bundle install
```

---

## Démarrage rapide

```ruby
require "enconvert"

client = Enconvert::Client.new(api_key: ENV.fetch("ENCONVERT_API_KEY"))

# Convertir une page en direct en PDF et l'écrire sur le disque.
result = client.convert_url_to_pdf("https://example.com", save_to: "page.pdf")
puts result.presigned_url

# Lire la même page comme un agent devrait le faire, avec un score de qualité attaché.
op = client.v2.perceive("https://example.com", outputs: %w[markdown structured])
puts op.outputs["markdown"].url, op.render_quality  # par ex. 0.93
```

La gem est côté serveur uniquement. Votre clé API privée doit rester sur le serveur : lisez-la depuis une variable d'environnement ou un gestionnaire de secrets, et ne la livrez jamais à un navigateur ni à un binaire mobile.

---

## Ce que le client expose

`Enconvert::Client` porte directement la surface de conversion de fichiers, et toute la surface de web intelligence est accrochée à `client.v2`.

| Groupe | Méthodes | Renvoie |
|-------|---------|---------|
| URL unique | `convert_url_to_pdf`, `convert_url_to_screenshot`, `convert_url_to_markdown` | `ConversionResult` |
| Fichier envoyé | `convert_image`, `convert_document`, `convert_to_markdown`, `convert_to_pdf` | `ConversionResult` |
| Lot sur un site entier | `convert_website_to_pdf`, `convert_website_to_screenshot` | `BatchSubmission` |
| Statut et interrogation | `get_job_status`, `get_batch_status`, `wait_for_batch` | `JobStatus`, `BatchStatus` |
| Web intelligence | `client.v2.*`, 23 méthodes réparties sur six capacités | Structs V2 |
| Aides sur les formats | `Enconvert.valid_outputs_for`, `Enconvert::IMPLEMENTED_CONVERSIONS` | `Array`, `Set` |

Chaque type de réponse est un `Struct` créé avec `keyword_init: true` : vous lisez donc les champs comme de simples méthodes, par exemple `result.presigned_url`, `op.render_quality`, `job.total_chunks`.

---

## Conversion de fichiers

### convert_url_to_pdf

Rend n'importe quelle URL publique en PDF.

```ruby
result = client.convert_url_to_pdf(
  "https://example.com",
  single_page: false,
  pdf_options: { page_size: "A4", orientation: "landscape" },
  viewport_width: 1440,
  save_to: "report.pdf"
)

puts result.filename, result.file_size
```

| Option | Type | Par défaut | Description |
|--------|------|---------|-------------|
| `save_to` | `String` | `nil` | Chemin local vers lequel télécharger le PDF. Les répertoires parents sont créés pour vous. |
| `single_page` | `Boolean` | `true` | `true` produit une seule page continue. `false` pagine en utilisant `pdf_options[:page_size]`. |
| `pdf_options` | `Hash` | `nil` | Géométrie de page. Voir [Options PDF](#options-pdf). |
| `viewport_width` | `Integer` | `1920` | Largeur de la fenêtre d'affichage du navigateur, en pixels. |
| `viewport_height` | `Integer` | `1080` | Hauteur de la fenêtre d'affichage du navigateur, en pixels. |
| `load_media` | `Boolean` | `true` | Attend les images et les vidéos avant la capture. |
| `enable_scroll` | `Boolean` | `true` | Fait défiler la page de haut en bas pour déclencher les chargements différés. |
| `output_filename` | `String` | `nil` | Remplace le nom de fichier généré. |
| `auth` | `Hash` | `nil` | Identifiants HTTP basic, par exemple `{ username: "user", password: "pass" }`. |
| `cookies`, `headers` | `Array`, `Hash` | `nil` | Cookies à définir et en-têtes de requête supplémentaires. Les trois champs d'accès navigateur sont transmis sans modification. |

<div class="alert alert-warning">
<strong>Ne combinez pas <code>auth</code> avec un en-tête <code>Authorization</code>.</strong> L'API rejette ce conflit, choisissez donc l'un ou l'autre.
</div>

### convert_url_to_screenshot

Capture un PNG pleine page de n'importe quelle URL.

```ruby
client.convert_url_to_screenshot("https://example.com", viewport_width: 1440, save_to: "shot.png")
```

Accepte les mêmes options de fenêtre d'affichage, de médias, de défilement, de nom de fichier et d'accès navigateur que `convert_url_to_pdf`. Elle ne prend pas `single_page` ni `pdf_options`.

### convert_url_to_markdown

Extrait du Markdown GitHub-Flavored propre depuis n'importe quelle 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.

```ruby
client.convert_url_to_markdown("https://example.com/article", save_to: "article.md")
```

Utile pour alimenter un pipeline RAG, importer du contenu tiers dans un CMS, ou constituer un corpus d'entraînement.

### convert_image

Convertit entre `jpeg`, `png`, `svg`, `heic` et `webp` dans n'importe quel sens, ou rastérise un PDF en JPEG.

```ruby
# Depuis un chemin sur le disque.
client.convert_image("photo.heic", output_format: "webp", save_to: "photo.webp")

# Depuis des octets que vous détenez déjà.
bytes = File.binread("photo.heic")
client.convert_image({ data: bytes, filename: "photo.heic" }, output_format: "webp", save_to: "photo.webp")

# Rastériser la première page d'un PDF.
client.convert_image("invoice.pdf", output_format: "jpeg", save_to: "invoice.jpg")
```

Le format d'entrée est déduit de l'extension du nom de fichier : un `IO` brut sans nom de fichier ne peut donc pas être utilisé ici. Passez plutôt un chemin ou un Hash `{ data:, filename: }`.

| Option | Type | Requis | Description |
|--------|------|----------|-------------|
| `output_format` | `String` | Oui | Format cible. Les alias `jpg`, `yml`, `htm` et `md` sont normalisés pour vous. |
| `save_to` | `String` | Non | Chemin local vers lequel télécharger le résultat. |
| `output_filename` | `String` | Non | Remplace le nom de fichier généré. |

### convert_document

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

```ruby
# docx vers pdf
client.convert_document("report.docx", save_to: "report.pdf")

# json vers yaml
client.convert_document("data.json", output_format: "yaml", save_to: "data.yaml")

# markdown vers pdf avec une mise en page personnalisée
client.convert_document(
  "README.md",
  output_format: "pdf",
  pdf_options: { page_size: "A4", margins: { top: 20, bottom: 20, left: 25, right: 25 } },
  save_to: "readme.pdf"
)
```

**Extensions d'entrée reconnues :** `.doc`, `.docx`, `.xls`, `.xlsx`, `.ppt`, `.pptx`, `.html`, `.htm`, `.odt`, `.ods`, `.odp`, `.ots`, `.pages`, `.numbers`, `.md`, `.markdown`, `.csv`, `.json`, `.xml`, `.yaml`, `.yml`, `.toml`.

L'EPUB n'a pas de paire de conversion documentaire dédiée. Faites plutôt passer les fichiers `.epub` par `convert_to_pdf` ou `convert_to_markdown`.

| Option | Type | Par défaut | Description |
|--------|------|---------|-------------|
| `output_format` | `String` | `"pdf"` | Format cible. |
| `save_to` | `String` | `nil` | Chemin local vers lequel télécharger le résultat. |
| `output_filename` | `String` | `nil` | Remplace le nom de fichier généré. |
| `pdf_options` | `Hash` | `nil` | Mise en page, prise en compte lorsque la sortie est un PDF. |

### convert_to_markdown

Convertit en Markdown propre un fichier envoyé, dans presque n'importe quel format de document. Le format est détecté sur le serveur.

```ruby
client.convert_to_markdown("handbook.docx", save_to: "handbook.md")
```

**Entrées acceptées :** PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT et MD, ainsi que les formats bureautiques hérités et ODF. Les images ne sont pas prises en charge sur cet endpoint.

La sortie est un unique fichier `.md` qui respecte la hiérarchie des titres, ce qui en fait une bonne brique pour l'ingestion RAG : un découpeur sémantique peut segmenter sur la hiérarchie de titres du document plutôt que sur un nombre de caractères arbitraire. Les seules options sont `save_to:` et `output_filename:`.

### convert_to_pdf

Convertit en PDF un fichier envoyé, dans presque n'importe quel format.

```ruby
# pptx vers pdf
client.convert_to_pdf("slides.pptx", save_to: "slides.pdf")

# pdf transmis tel quel, normalisé en niveaux de gris
client.convert_to_pdf("scan.pdf", pdf_options: { grayscale: true }, save_to: "gray.pdf")
```

**Entrées acceptées :** formats bureautiques, ODF, Pages, Numbers, RTF, CSV, HTML, Markdown, texte brut, images matricielles, SVG, EPUB, ou un PDF existant qui est transmis tel quel et normalisé.

<div class="alert alert-warning">
<strong>Seul <code>pdf_options[:grayscale]</code> est pris en compte ici.</strong> Les autres champs de géométrie de page sont ignorés sur cet endpoint. Lorsque vous avez besoin d'un contrôle complet sur la taille de page, l'orientation et les marges, utilisez <code>convert_document</code> ou <code>convert_url_to_pdf</code>.
</div>

Les options sont `save_to:`, `output_filename:` et `pdf_options:` (niveaux de gris uniquement).

### convert_website_to_pdf et convert_website_to_screenshot

Découvrent chaque page d'un site web, convertissent chacune d'elles en arrière-plan, et rassemblent les résultats dans une seule archive ZIP. Les deux méthodes sont asynchrones et renvoient immédiatement un `BatchSubmission`.

```ruby
batch = client.convert_website_to_pdf(
  "https://example.com",
  crawl_mode: "sitemap",
  exclude_patterns: ["/tag/"],
  notification_email: "me@example.com"
)

status = client.wait_for_batch(batch.batch_id, save_to: "site.zip")
puts "#{status.completed} of #{status.total} pages converted"
status.items.each { |item| puts "#{item.status}\t#{item.source_url}" }
```

`convert_website_to_screenshot` fonctionne à l'identique et produit un ZIP de PNG.

| Option | Type | Description |
|--------|------|-------------|
| `crawl_mode` | `String` | Manière dont les pages sont découvertes, par exemple `"sitemap"`. |
| `include_patterns`, `exclude_patterns` | `Array` | Ne crawler, ou au contraire ignorer, que les URL correspondant à ces motifs. |
| `notification_email` | `String` | Adresse e-mail à prévenir à la fin du lot. |
| `callback_url` | `String` | Webhook appelé à la fin du traitement. |
| `output_filename` | `String` | Remplace le nom de fichier ZIP généré. |
| `viewport_width`, `viewport_height`, `load_media`, `enable_scroll` | `Integer`, `Boolean` | Comportement de rendu par page. Envoyé uniquement si vous le définissez, sinon la passerelle applique ses propres valeurs par défaut. |
| `auth`, `cookies`, `headers` | `Hash`, `Array`, `Hash` | Accès navigateur pour les pages protégées. |
| `single_page`, `pdf_options` | `Boolean`, `Hash` | Lots PDF uniquement. |

`wait_for_batch(batch_id, interval: 5, timeout: 1800, save_to: nil)` interroge jusqu'à ce que le lot quitte l'état `"processing"`, puis renvoie le `BatchStatus` final. Avec `save_to`, elle télécharge aussi le ZIP. Elle lève `Enconvert::APIError` avec le statut `504` quand le délai est atteint, et le statut `500` quand un lot se termine sans ZIP à enregistrer.

### get_job_status

Interroge un job de conversion unique par son identifiant.

```ruby
status = client.get_job_status("job_abc123")
case status.status
when "success" then puts status.presigned_url
when "failed"  then warn status.error
else                puts "still processing"
end
```

Renvoie un `JobStatus` avec `status`, `presigned_url`, `object_key` et `error`. Vous avez rarement besoin de l'appeler vous-même, puisque le SDK l'interroge déjà pour vous. Voir [Récupération des timeouts](#recuperation-des-timeouts).

### Conversions prises en charge

La gem embarque le tableau complet des endpoints `{input}-to-{output}` implémentés et valide chaque paire localement : une paire non prise en charge lève donc `Enconvert::Error` avant qu'aucune requête ne quitte votre processus.

```ruby
Enconvert.valid_outputs_for("json")      # => ["csv", "toml", "xml", "yaml"]
Enconvert.valid_outputs_for("pdf")       # => ["jpeg"]
Enconvert::IMPLEMENTED_CONVERSIONS.size  # => 43
```

| Entrée | Sorties |
|-------|---------|
| `json` | `csv`, `toml`, `xml`, `yaml` |
| `xml` | `csv`, `json` |
| `yaml` | `json` |
| `csv` | `json`, `xml` |
| `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` |

Cela fait 43 endpoints : 13 conversions de texte structuré, 9 conversions de documents et 21 conversions d'images.

---

## Web intelligence (V2)

`client.v2` transforme des pages web en direct en données prêtes pour un agent : rendre, énumérer, rechercher, extraire, ingérer et surveiller. Chaque rendu porte `render_quality`, un score de 0.0 à 1.0 attaché à chaque lecture. Un score bas signifie que la page ne s'est pas rendue proprement, typiquement une page de challenge, un mur de cookies, une barrière de connexion ou une coquille JavaScript vide. Le contenu est tout de même renvoyé, mais signalé, avec un tableau `warnings` et un Hash `deductions` qui indique quels contrôles se sont déclenchés. Lisez le score avant de placer le contenu où que ce soit, et une mauvaise lecture n'entrera jamais silencieusement dans le contexte de votre agent.

Tous les endpoints V2 exigent une clé API privée. Voir la [vue d'ensemble V2](/fr/docs/v2-overview) pour la référence au niveau des endpoints.

### Perceive

Rend une URL vers les artefacts que vous demandez. `perceive` est synchrone et renvoie l'opération terminée avec des URL d'artefacts fraîchement signées.

```ruby
op = client.v2.perceive(
  "https://example.com",
  outputs: %w[markdown screenshot structured],
  extract: %w[tables metadata],
  only_main_content: true
)

puts op.operation_id, op.render_quality          # "per_...", puis de 0.0 à 1.0
puts op.outputs["markdown"].url                  # URL d'artefact signée
puts op.outputs["markdown"].expires_in           # secondes avant qu'elle cesse de fonctionner
puts op.structured, op.warnings.inspect          # Hash Ruby simple, puis Array de String
```

Resignez à tout moment les URL d'artefacts d'une opération antérieure :

```ruby
again = client.v2.get_perceive_operation(op.operation_id)
```

Percevez jusqu'à 1000 URL avec un seul bloc d'options partagé. Les petits lots s'exécutent en ligne et reviennent terminés. Les plus gros reviennent avec le statut `"queued"`, il faut donc les interroger par `job_id` :

```ruby
batch = client.v2.perceive_batch(
  ["https://a.example.com", "https://b.example.com"],
  outputs: %w[markdown],
  output_mode: "zip"
)

done = client.v2.get_perceive_batch(batch.job_id)
puts "#{done.completed}/#{done.total} complete, #{done.failed} failed"
puts done.zip&.url
```

Évitez complètement l'aller-retour par URL signée avec `perceive_direct`, où le corps de la réponse HTTP est l'artefact lui-même :

```ruby
direct = client.v2.perceive_direct("https://example.com", outputs: %w[markdown])
puts direct.filename, direct.content_type, direct.content.bytesize
File.binwrite("page.md", direct.content)

# Retélécharger un artefact stocké d'une opération antérieure.
raw = client.v2.download_perceive_artifact(op.operation_id, output: "markdown")
```

`perceive_direct` a besoin d'exactement une sortie produisant un artefact et lève sinon `Enconvert::Error` localement. `structured` peut accompagner la demande, car il reste en ligne côté serveur et n'est jamais diffusé. `download_perceive_artifact` prend un `output:` facultatif, que vous pouvez omettre lorsque l'opération n'a produit qu'un seul artefact et que vous devez nommer lorsqu'elle en a produit plusieurs, et elle renvoie `410` une fois l'artefact sorti de sa fenêtre de rétention.

| Option | Type | Description |
|--------|------|-------------|
| `outputs` | `Array` | Une valeur parmi `markdown`, `html_cleaned`, `html_raw`, `screenshot`, `screenshot_full_page`, `pdf`, `links`, `images`, `structured`. Vaut `["markdown", "structured"]` par défaut côté serveur. |
| `extract` | `Array` | Une valeur parmi `tables`, `prices`, `contacts`, `metadata`, `main_content`, `headings`, `structured_data`, `technologies`, `all`. |
| `schema` | `Hash` | Descriptions de champs pour l'extraction structurée. |
| `wait_for`, `wait_timeout_ms` | `String`, `Integer` | Sélecteur CSS à attendre avant la capture, et plafond de cette attente. |
| `js_code` | `String` | JavaScript à exécuter dans la page avant la capture. |
| `viewport`, `mobile` | `Hash`, `Boolean` | Dimensions de la fenêtre d'affichage, et rendu ou non avec un profil mobile. |
| `headers`, `cookies`, `auth` | `Hash`, `Array`, `Hash` | Accès navigateur pour les pages protégées. |
| `proxy_url` | `String` | Fait passer le rendu par votre propre proxy. |
| `geolocation` | `Hash` | Géolocalisation simulée pour le contexte navigateur. |
| `action_chain` | `Array` | Clics, défilements et saisies scriptés avant la capture. |
| `cache_mode` | `String` | `enabled`, `bypass` ou `refresh`. |
| `pdf_options` | `Hash` | Géométrie de page lorsque `pdf` figure parmi les sorties. |
| `block_resources` | `Array` | Types de ressources à bloquer, par exemple `image`, `font`, `script`. |
| `respect_robots` | `Boolean` | Respecte `robots.txt`. |
| `only_main_content` | `Boolean` | Supprime la navigation et l'habillage de la sortie Markdown. |
| `direct_download` | `Boolean` | Accepté par `perceive` uniquement. `perceive_batch` le rejette. |

La sémantique complète des paramètres est détaillée dans [Perceive](/fr/docs/v2-perceive) et [Paramètres et options](/fr/docs/parameters-options).

### Discover

Énumère les URL d'un site sans rien rendre. Aucun navigateur n'intervient, c'est donc rapide et peu coûteux.

```ruby
found = client.v2.discover(
  "https://example.com",
  mode: "hybrid",            # "sitemap", "crawl" ou "hybrid"
  max_urls: 200,
  max_depth: 3,
  exclude_patterns: ["/tag/"],
  same_domain_only: true,
  respect_robots: true
)

puts found.total, found.urls.first(10)
puts found.truncated        # true quand max_urls a plafonné le résultat
puts found.sources.inspect  # d'où vient chaque URL
```

Voir [Discover](/fr/docs/v2-discover) pour le comportement mode par mode.

### Lookup

Lance une recherche web catégorisée, et perçoit éventuellement les meilleurs résultats dans le même appel.

```ruby
search = client.v2.lookup(
  "best static site generators",
  category: "web",           # web, news, images, scholar, patents, maps
  num_results: 10,
  time_filter: "month",      # hour, day, week, month, year
  perceive_top: 3
)

search.results.each do |hit|
  puts hit.position, hit.title, hit.url
  puts hit.perceive&.render_quality  # présent pour les résultats perçus automatiquement
end

puts search.answer_box.inspect, search.knowledge_graph.inspect
```

Avec `perceive_top: 3`, les trois premières URL de résultats sont rendues et portent un `PerceiveResult` complet en ligne sur `hit.perceive`. Leurs identifiants d'opération sont également rassemblés dans `search.perceive_operation_ids`. Plus de détails dans [Lookup](/fr/docs/v2-lookup).

### Distill

Extraction structurée pilotée par schéma. Fournissez exactement l'un de `urls:` ou `discover_from:`, et `schema:` est toujours obligatoire. Le SDK lève `Enconvert::Error` localement si vous vous trompez.

```ruby
extraction = client.v2.distill(
  urls: ["https://example.com/pricing"],
  schema: { plans: "list of plan names with monthly prices" },
  css_schema: {
    base_selector: ".plan-card",
    fields: [
      { name: "name",  type: "text", selector: "h3" },
      { name: "price", type: "text", selector: ".price" }
    ]
  }
)

item = extraction.results.first
puts item.data.inspect
puts item.extraction_tier   # "css", "llm", "mixed" ou "none"
puts item.fields_from_css, item.fields_from_llm
```

Le `css_schema` facultatif s'exécute en premier et répond à tout ce qu'il peut à partir des seuls sélecteurs. Seuls les champs qu'il manque escaladent vers le niveau modèle de langage, ce qui explique pourquoi `fields_from_css` et `fields_from_llm` sont rapportés séparément.

Découvrir et distiller en un seul appel :

```ruby
client.v2.distill(
  discover_from: { url: "https://example.com", mode: "sitemap", max_pages: 10 },
  schema: { title: "page title", summary: "one-line summary" }
)
```

Les types de champs CSS sont `text`, `attribute`, `html`, `regex`, `nested`, `list` et `nested_list`. Les listes de champs imbriquées sont sérialisées récursivement. Voir [Distill](/fr/docs/v2-distill).

### Ingest

Transforme un site entier, ou un ensemble de documents envoyés, en JSONL découpé et prêt pour le RAG, à travers un seul pipeline. Ingest est toujours asynchrone.

```ruby
job = client.v2.ingest(
  mode: "sitemap",           # urls, sitemap, crawl
  url: "https://docs.example.com",
  max_pages: 100,
  chunk: { max_words: 512, sentence_overlap: 1 },
  webhook_url: "https://my.app/hooks/enconvert"
)

status = client.v2.get_ingest_job(job.job_id)
puts status.status                                   # queued, discovering, processing, completed, failed, canceled
puts status.pages_processed, status.total_chunks
puts status.output_url if status.status == "completed"  # JSONL
```

Le mode `"urls"` prend une liste `urls:` et rejette `url:`. Tous les autres modes prennent une `url:` de départ et rejettent `urls:`. Les deux règles sont appliquées localement avant l'envoi de la requête.

Envoyer des fichiers plutôt que crawler :

```ruby
file_job = client.v2.ingest_files(
  ["handbook.pdf", "notes.docx"],
  chunk: { max_words: 512, sentence_overlap: 1 }
)
```

`ingest_files` accepte les formats PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT et MD, ainsi que les formats bureautiques hérités et ODF. Chaque entrée peut être un chemin, un objet de type IO, ou un Hash `{ data:, filename: }`.

Gérer les jobs et la livraison des webhooks :

```ruby
list = client.v2.list_ingest_jobs(limit: 20)
list.jobs.each { |j| puts "#{j.job_id}\t#{j.status}\t#{j.total_chunks}" }
puts list.has_more

client.v2.cancel_ingest_job(job.job_id)   # idempotent

secret = client.v2.get_webhook_secret
puts secret.secret, secret.signature_header, secret.timestamp_header,
     secret.signature_scheme, secret.replay_tolerance_seconds

client.v2.rotate_webhook_secret            # les anciennes signatures cessent aussitôt d'être valides
client.v2.retry_ingest_webhook(job.job_id) # relivrer le webhook d'un job terminé
```

`retry_ingest_webhook` renvoie un `WebhookRetryResult` avec `delivered`, `attempts`, `status_code` et `detail`. Elle répond `409` lorsque le job n'est pas terminé et `400` lorsque le job n'a aucun webhook configuré. Voir [Ingest](/fr/docs/v2-ingest).

### Watch

Refait le rendu d'une URL à intervalle fixe et vous prévient quand elle change.

```ruby
watcher = client.v2.create_watcher(
  "https://example.com/pricing",
  frequency_minutes: 60,     # plancher horaire
  diff_mode: "auto",         # auto, text, structured, tables, metadata
  webhook_url: "https://my.app/hooks/changes",
  notify_email: true
)

puts watcher.watcher_id, watcher.next_check_at

history = client.v2.get_watcher_snapshots(watcher.watcher_id, limit: 10)
history.snapshots.each do |snap|
  puts snap.checked_at, snap.has_changes, snap.similarity, snap.change_count, snap.changes.inspect
end

client.v2.list_watchers(limit: 20)
client.v2.get_watcher(watcher.watcher_id)
client.v2.update_watcher(watcher.watcher_id, status: "paused")
client.v2.update_watcher(watcher.watcher_id, webhook_url: "")  # efface le webhook
client.v2.delete_watcher(watcher.watcher_id)                   # suppression logique, idempotente
```

`update_watcher` lève `Enconvert::Error` si vous l'appelez sans aucun champ à mettre à jour. `delete_watcher` renvoie le watcher marqué avec le statut `"deleted"`, et un watcher supprimé se relit en `404`. Sémantique complète dans [Watch](/fr/docs/v2-watch).

---

## Options PDF

`pdf_options` est un simple Hash à clés symboles, accepté par `convert_url_to_pdf`, `convert_website_to_pdf`, `convert_document`, `convert_to_pdf` (niveaux de gris uniquement), et `client.v2.perceive` lorsque `pdf` figure parmi les sorties. Seules les clés que vous définissez sont envoyées sur le réseau.

```ruby
client.convert_url_to_pdf(
  "https://example.com",
  pdf_options: { page_size: "A4", orientation: "landscape", scale: 0.9,
                 margins: { top: 10, bottom: 10, left: 15, right: 15 } },
  save_to: "report.pdf"
)
```

| Clé | Type | Description |
|-----|------|-------------|
| `page_size` | `String` | `"A4"`, `"A3"`, `"Letter"`, `"Legal"`, et similaires. |
| `page_width` | `Number` | Largeur de page explicite, comme alternative à `page_size`. |
| `page_height` | `Number` | Hauteur de page explicite. |
| `orientation` | `String` | `"portrait"` ou `"landscape"`. |
| `margins` | `Hash` | `{ top:, bottom:, left:, right: }`, tous facultatifs. |
| `scale` | `Number` | Échelle de rendu, par exemple `0.9` pour 90 pour cent. |
| `grayscale` | `Boolean` | Post-traite le PDF en niveaux de gris. |
| `header` | `Hash` | Texte d'en-tête par zone de page. |
| `footer` | `Hash` | Texte de pied de page par zone de page. |

---

## Gestion des erreurs

Chaque échec est une `Enconvert::Error` ou l'une de ses sous-classes : vos clauses `rescue` peuvent donc être aussi larges ou aussi étroites que vous le souhaitez. Les échecs de validation côté client, comme une paire de conversion non prise en charge ou un `schema` manquant, lèvent la classe de base `Enconvert::Error` avant qu'aucune requête HTTP ne soit émise.

```ruby
begin
  client.v2.perceive("https://example.com")
rescue Enconvert::AuthenticationError
  warn "Invalid or missing API key"
rescue Enconvert::QuotaError
  warn "Request was rejected with 402"
rescue Enconvert::RateLimitError
  warn "Too many requests, back off and retry"
rescue Enconvert::APIError => e
  warn "API error [#{e.status_code}]: #{e.message}"
rescue Enconvert::Error => e
  warn "Client-side validation failed: #{e.message}"
end
```

| Classe | Déclenchée sur | Code de statut |
|-------|-----------|-------------|
| `Enconvert::AuthenticationError` | Clé API invalide, manquante ou révoquée | `401`, `403` |
| `Enconvert::QuotaError` | Déclenchée sur un HTTP 402 | `402` |
| `Enconvert::RateLimitError` | Trop de requêtes | `429` |
| `Enconvert::APIError` | Toute autre réponse de 400 ou plus | le code réel |
| `Enconvert::Error` | Classe de base, plus tous les échecs de validation locale | aucun |

`APIError#status_code` vous donne le statut HTTP, et le message est extrait du champ `detail` ou `error` du corps de la réponse lorsque celui-ci est en JSON. La table complète des messages se trouve dans [Codes d'erreur](/fr/docs/error-codes).

---

## Récupération des timeouts

Un rendu URL-vers-PDF long ou une conversion de document volumineux peut survivre au timeout du reverse proxy même quand la conversion elle-même réussit sur le serveur. Le SDK s'en remet de façon transparente, sans aucun code de votre côté.

1. Avant chaque requête de conversion, le client génère un identifiant de job de 32 caractères et l'envoie comme `job_id` dans le corps ou dans le formulaire multipart.
2. Si la requête revient avec un statut de 500 ou plus, le client bascule vers le polling de `GET /v1/convert/status/{job_id}` toutes les 3 secondes. Un `404` obtenu pendant que la ligne du job s'écrit encore est ignoré, et l'interrogation continue.
3. Dès que le job affiche `success`, le résultat est renvoyé. Dès qu'il affiche `failed`, `Enconvert::APIError` est levée avec le statut `500` et le message d'erreur du serveur.
4. Le délai maximal de polling est de 300 secondes. Au-delà, le client lève `Enconvert::APIError` avec le statut `504` et le message `Conversion timed out`.

Le `job_id` généré par le client est également fusionné dans chaque réponse réussie : `result.job_id` est donc toujours renseigné et vous pouvez interroger `get_job_status` vous-même plus tard si vous le souhaitez.

<div class="alert alert-info">
<strong>Les soumissions de lots de site entier ne sont pas concernées.</strong> <code>convert_website_to_pdf</code> et <code>convert_website_to_screenshot</code> ne créent pas de ligne de job : un 5xx y signifie donc que la soumission elle-même a échoué, et l'erreur remonte immédiatement au lieu d'être interrogée.
</div>

---

## Configuration

```ruby
client = Enconvert::Client.new(
  api_key: ENV.fetch("ENCONVERT_API_KEY"),
  timeout: 300,
  base_url: "https://api.enconvert.com"
)
```

| Option | Type | Par défaut | Description |
|--------|------|---------|-------------|
| `api_key` | `String` | obligatoire | Votre clé API privée. Une valeur `nil` ou vide lève `Enconvert::Error` à la construction. |
| `timeout` | `Integer` | `300` | Timeout d'ouverture et de lecture en **secondes**, appliqué à chaque requête. |
| `base_url` | `String` | `"https://api.enconvert.com"` | URL de base de l'API. Les barres obliques finales sont supprimées. À remplacer pour une passerelle auto-hébergée. |

Les requêtes s'authentifient avec l'en-tête `X-API-Key`. Lorsque vous passez `save_to`, le téléchargement contourne volontairement cet en-tête, car l'URL de stockage présignée ne doit pas recevoir votre clé API.

<div class="alert alert-warning">
<strong>Ne codez jamais la clé API en dur.</strong> Lisez-la depuis une variable d'environnement, les credentials Rails ou votre gestionnaire de secrets. Quiconque détient votre clé privée peut lancer des conversions à votre place. Faites-la tourner depuis le <a href="/fr/dashboard">tableau de bord</a> en cas de fuite.
</div>

---

## Structure du résultat

Chaque conversion de fichier unique et de page unique renvoie un `Enconvert::ConversionResult` :

```ruby
result = client.convert_document("report.docx", save_to: "report.pdf")

result.presigned_url            # URL signée pour la sortie convertie
result.object_key               # clé de l'objet de stockage
result.filename                 # nom de fichier côté serveur
result.file_size                # octets, ou nil
result.conversion_time_seconds  # secondes, ou nil
result.job_id                   # toujours renseigné par le client
```

Les autres Structs V1 sont `JobStatus` (`status`, `presigned_url`, `object_key`, `error`), `BatchSubmission` (`batch_id`, `status`, `url_count`, `total_discovered`, `discovery_method`, `output_format`), `BatchStatus` (`batch_id`, `status`, `total`, `completed`, `failed`, `in_progress`, `output_mode`, `zip_download_url`, `items`), et `BatchItem` (`source_url`, `status`, `download_url`, `output_file_size`, `duration`).

Côté V2, `PerceiveResult` est celui à connaître :

```ruby
op.operation_id                              # "per_..."
op.status                                    # queued, processing, completed, failed
op.url, op.url_final, op.content_hash
op.render_quality, op.cache_hit              # de 0.0 à 1.0 (ou nil), et s'il vient du cache
op.outputs                                   # Hash de nom => V2OutputArtifact
op.structured, op.extraction_tier            # Hash (ou nil), et comment il a été extrait
op.tokens.input, op.tokens.output, op.cost_cents, op.duration_ms
op.error, op.warnings                        # String ou nil, et Array de String
op.status_code                               # statut HTTP amont de la page
op.deductions, op.options_echo               # pourquoi render_quality a baissé, et les options utilisées
```

Un `V2OutputArtifact` porte `url`, `object_key`, `size_bytes`, `content_type` et `expires_in` (900 secondes par défaut) : les URL d'artefacts signées sont donc de courte durée. Resignez-les avec `get_perceive_operation`, ou téléchargez les octets avec `download_perceive_artifact`. Les charges utiles fournies par l'utilisateur, comme les schémas, les données extraites, les champs suivis et les changements de diff, passent sans modification sous forme de simples Hash Ruby.

Les URL présignées des conversions V1 sont elles aussi temporaires. Passez `save_to` quand vous voulez les octets sur le disque, ou copiez-les dans votre propre bucket pour un stockage permanent.

---

## Source et problèmes

- **RubyGems :** [enconvert](https://rubygems.org/gems/enconvert)
- **GitHub :** [conversionapi/ruby-sdk](https://github.com/conversionapi/ruby-sdk)
- **Ruby :** 3.0.0 ou plus récent, aucune dépendance à l'exécution
- **Licence :** MIT
- **Autres langages :** [Tous les SDK](/fr/docs/sdks)
- **Référence de l'API :** [Vue d'ensemble des endpoints](/fr/docs/endpoints-overview), [Authentification](/fr/docs/authentication)

---

## Questions fréquentes

### Comment convertir des fichiers en Ruby avec une gem ?

Exécutez `gem install enconvert`, construisez un client avec `Enconvert::Client.new(api_key: ENV.fetch("ENCONVERT_API_KEY"))`, et appelez une méthode comme `convert_document`, `convert_image` ou `convert_url_to_pdf`. Passez `save_to:` et le SDK télécharge le résultat vers ce chemin pour vous, en créant les répertoires parents au besoin.

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

`client.convert_document("report.docx", save_to: "report.pdf")`. Le format de sortie vaut `"pdf"` par défaut, vous n'avez donc besoin d'`output_format:` que lorsque vous voulez autre chose. Le même appel fonctionne pour `.doc`, `.xls`, `.xlsx`, `.ppt`, `.pptx`, `.odt`, `.ods`, `.odp`, `.ots`, `.pages` et `.numbers`.

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

`client.convert_url_to_pdf("https://example.com", save_to: "page.pdf")`. Définissez `single_page: false` pour paginer au lieu de produire une seule page continue, et passez `pdf_options:` pour la taille de page, l'orientation, les marges et l'échelle. Pour une page derrière une authentification HTTP basic, ajoutez `auth: { username: ..., password: ... }`.

### Comment convertir du HEIC en WebP en Ruby ?

`client.convert_image("photo.heic", output_format: "webp", save_to: "photo.webp")`. Le format d'entrée vient de l'extension du nom de fichier, et la gem convertit librement entre `jpeg`, `png`, `svg`, `heic` et `webp`. Elle rastérise également un PDF en JPEG avec `output_format: "jpeg"`.

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

Deux options. `client.convert_url_to_markdown(url, save_to: "article.md")` vous donne du Markdown GitHub-Flavored propre avec un frontmatter YAML. `client.v2.perceive(url, outputs: %w[markdown])` vous donne le même contenu plus un score `render_quality`, des avertissements et une extraction structurée facultative, ce qui est exactement ce qu'il vous faut quand un agent va lire le résultat.

### Que signifie render_quality et pourquoi devrais-je le vérifier ?

C'est un score de 0.0 à 1.0 attaché à chaque lecture V2, qui reflète à quel point la page s'est réellement rendue proprement. Une page de challenge, un mur de cookies, une barrière de connexion, une page d'erreur HTTP ou une coquille JavaScript vide obtiennent toutes un score bas. Le contenu est tout de même renvoyé pour que vous puissiez l'inspecter, avec `warnings` et `deductions` qui expliquent le score. Filtrez dessus avant de stocker le texte ou de l'envoyer à un modèle.

### Le SDK Ruby réessaie-t-il quand une conversion longue arrive à expiration ?

Oui. Chaque requête de conversion porte un `job_id` généré par le client, et si la requête échoue avec un statut de 500 ou plus, le SDK interroge `GET /v1/convert/status/{job_id}` toutes les 3 secondes pendant 300 secondes au maximum. Il renvoie le résultat une fois le job réussi et lève `Enconvert::APIError` avec le statut `504` si le délai expire. Les soumissions de lots de site entier sont exclues, parce qu'elles n'ont pas de ligne de job.

### Que se passe-t-il si je demande une paire de conversion que l'API n'implémente pas ?

La gem lève `Enconvert::Error` localement, avant tout appel réseau, et le message liste les sorties valides pour cette entrée. Vous pouvez consulter la même table vous-même avec `Enconvert.valid_outputs_for("json")`, qui renvoie `["csv", "toml", "xml", "yaml"]`.

### Puis-je utiliser la gem enconvert dans Rails ou dans un job en arrière-plan ?

Oui, et un job en arrière-plan est le bon endroit pour cela. La gem repose sur `net/http` nu, sans dépendance à l'exécution ni état global : une instance d'`Enconvert::Client` peut donc être construite par job ou mémoïsée par processus en toute sécurité. Les conversions longues bloquent le thread appelant jusqu'au `timeout` configuré, qui vaut 300 secondes par défaut : gardez-les donc hors du cycle d'une requête web.

### Quelles versions de Ruby le SDK prend-il en charge ?

Ruby 3.0.0 et plus récent, comme déclaré par `required_ruby_version` dans le gemspec. Il n'y a aucune dépendance de gem à l'exécution : la gem s'installe donc dans n'importe quel groupe Bundler sans traîner tout un arbre de dépendances derrière elle.

### Comment vérifier la signature d'un webhook ingest ?

Appelez `client.v2.get_webhook_secret`, qui crée le secret de signature du projet à la première utilisation et le renvoie avec `signature_header`, `timestamp_header`, `signature_scheme` et `replay_tolerance_seconds`. Calculez le HMAC sur le corps livré et comparez-le à l'en-tête de signature. Utilisez `rotate_webhook_secret` pour invalider l'ancien secret, et `retry_ingest_webhook(job_id)` pour relivrer la notification d'un job terminé.
