---
seo_title: SDK Ruby Conversione File | Client API RubyGems EnConvert
meta_desc: SDK Ruby ufficiale di EnConvert per Ruby 3.0+. Installa la gem enconvert per convertire file e per perceive, discover, distill, ingest e watch da Ruby.
keywords: sdk ruby conversione file, convertire file in ruby, url in pdf ruby, api web scraping ruby, docx in pdf ruby, sdk ruby enconvert, client api conversione file rubygems, heic in webp ruby, html in pdf gem ruby, pagina web in markdown ruby, estrazione dati strutturati ruby, gem ruby crawler siti
---

# SDK Ruby per la Conversione dei File

`enconvert` è la gem Ruby ufficiale per l'API EnConvert. Converte URL, immagini e documenti attraverso 43 endpoint di conversione implementati, ed espone un secondo namespace, `client.v2`, che legge le pagine web live in Markdown, JSON, screenshot e PDF pronti per gli agenti. Ogni lettura V2 porta con sé un punteggio `render_quality`, così una pagina bloccata o uno shell SPA vuoto non passano mai per contenuto reale. La gem richiede Ruby 3.0+ e non ha dipendenze a runtime, costruita su `net/http`, `json` e `securerandom` della libreria standard. Le risposte tornano come Struct con reader in snake_case.

<div class="alert alert-info">
<strong>RubyGems:</strong> <code>enconvert</code> · <strong>Sorgente:</strong> <a href="https://github.com/conversionapi/ruby-sdk">conversionapi/ruby-sdk</a> · <strong>Ruby:</strong> 3.0.0 o versioni successive · <strong>Dipendenze a runtime:</strong> nessuna
</div>

---

## Installazione

```bash
gem install enconvert
```

Oppure aggiungila al tuo `Gemfile`:

```ruby
gem "enconvert"
```

```bash
bundle install
```

---

## Avvio rapido

```ruby
require "enconvert"

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

# Converte una pagina live in PDF e la trasmette su disco.
result = client.convert_url_to_pdf("https://example.com", save_to: "page.pdf")
puts result.presigned_url

# Legge la stessa pagina come dovrebbe fare un agente, con un punteggio di qualità allegato.
op = client.v2.perceive("https://example.com", outputs: %w[markdown structured])
puts op.outputs["markdown"].url, op.render_quality  # ad es. 0.93
```

La gem è solo lato server. La tua chiave API privata deve restare sul server, quindi leggila da una variabile d'ambiente o da un secret manager e non spedirla mai a un browser o a un binario mobile.

---

## Cosa espone il client

`Enconvert::Client` porta direttamente la superficie di conversione dei file, e l'intera superficie di web intelligence pende da `client.v2`.

| Gruppo | Metodi | Restituisce |
|-------|---------|---------|
| URL singolo | `convert_url_to_pdf`, `convert_url_to_screenshot`, `convert_url_to_markdown` | `ConversionResult` |
| File caricato | `convert_image`, `convert_document`, `convert_to_markdown`, `convert_to_pdf` | `ConversionResult` |
| Batch di un intero sito | `convert_website_to_pdf`, `convert_website_to_screenshot` | `BatchSubmission` |
| Stato e polling | `get_job_status`, `get_batch_status`, `wait_for_batch` | `JobStatus`, `BatchStatus` |
| Web intelligence | `client.v2.*`, 23 metodi su sei funzionalità | Struct V2 |
| Helper sui formati | `Enconvert.valid_outputs_for`, `Enconvert::IMPLEMENTED_CONVERSIONS` | `Array`, `Set` |

Ogni tipo di risposta è una `Struct` creata con `keyword_init: true`, quindi leggi i campi come normali metodi: `result.presigned_url`, `op.render_quality`, `job.total_chunks`.

---

## Conversione dei file

### convert_url_to_pdf

Esegue il rendering di qualsiasi URL pubblico in 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
```

| Opzione | Tipo | Predefinito | Descrizione |
|--------|------|---------|-------------|
| `save_to` | `String` | `nil` | Percorso locale su cui scaricare il PDF. Le directory padre vengono create automaticamente. |
| `single_page` | `Boolean` | `true` | `true` produce una singola pagina continua. `false` pagina utilizzando `pdf_options[:page_size]`. |
| `pdf_options` | `Hash` | `nil` | Geometria di pagina. Vedi [Opzioni PDF](#opzioni-pdf). |
| `viewport_width` | `Integer` | `1920` | Larghezza del viewport del browser in pixel. |
| `viewport_height` | `Integer` | `1080` | Altezza del viewport del browser in pixel. |
| `load_media` | `Boolean` | `true` | Attende immagini e video prima della cattura. |
| `enable_scroll` | `Boolean` | `true` | Scorre dall'alto verso il basso perché scattino i caricamenti lazy. |
| `output_filename` | `String` | `nil` | Sovrascrive il nome file generato. |
| `auth` | `Hash` | `nil` | Credenziali HTTP basic, per esempio `{ username: "user", password: "pass" }`. |
| `cookies`, `headers` | `Array`, `Hash` | `nil` | Cookie da impostare e header di richiesta aggiuntivi. Tutti e tre i campi di accesso al browser passano inalterati. |

<div class="alert alert-warning">
<strong>Non combinare <code>auth</code> con un header <code>Authorization</code>.</strong> L'API rifiuta il conflitto, quindi scegli l'uno o l'altro.
</div>

### convert_url_to_screenshot

Cattura un PNG a pagina intera di qualsiasi URL.

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

Accetta le stesse opzioni di viewport, media, scroll, nome file e accesso al browser di `convert_url_to_pdf`. Non accetta `single_page` né `pdf_options`.

### convert_url_to_markdown

Estrae Markdown pulito in stile GitHub-Flavored da qualsiasi URL. Il convertitore rimuove navigazione, footer, pubblicità e script, mantiene il corpo principale dell'articolo e antepone un frontmatter YAML con titolo, descrizione, url, link e immagini.

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

Utile per alimentare una pipeline RAG, importare contenuti di terze parti in un CMS o costruire un corpus di addestramento.

### convert_image

Converte tra `jpeg`, `png`, `svg`, `heic` e `webp` in qualsiasi direzione, oppure rasterizza un PDF in JPEG.

```ruby
# Da un percorso su disco.
client.convert_image("photo.heic", output_format: "webp", save_to: "photo.webp")

# Da byte che hai già in mano.
bytes = File.binread("photo.heic")
client.convert_image({ data: bytes, filename: "photo.heic" }, output_format: "webp", save_to: "photo.webp")

# Rasterizza la prima pagina di un PDF.
client.convert_image("invoice.pdf", output_format: "jpeg", save_to: "invoice.jpg")
```

Il formato di input viene ricavato dall'estensione del nome file, quindi qui non si può usare un `IO` grezzo privo di nome file. Passa invece un percorso oppure un Hash `{ data:, filename: }`.

| Opzione | Tipo | Obbligatorio | Descrizione |
|--------|------|----------|-------------|
| `output_format` | `String` | Sì | Formato di destinazione. Gli alias `jpg`, `yml`, `htm` e `md` vengono normalizzati automaticamente. |
| `save_to` | `String` | No | Percorso locale su cui scaricare il risultato. |
| `output_filename` | `String` | No | Sovrascrive il nome file generato. |

### convert_document

Converte documenti e formati di dati. `output_format` vale `"pdf"` per impostazione predefinita.

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

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

# da markdown a pdf con impostazioni di pagina personalizzate
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"
)
```

**Estensioni di input riconosciute:** `.doc`, `.docx`, `.xls`, `.xlsx`, `.ppt`, `.pptx`, `.html`, `.htm`, `.odt`, `.ods`, `.odp`, `.ots`, `.pages`, `.numbers`, `.md`, `.markdown`, `.csv`, `.json`, `.xml`, `.yaml`, `.yml`, `.toml`.

EPUB non ha una coppia documentale dedicata. Invia invece i file `.epub` a `convert_to_pdf` oppure a `convert_to_markdown`.

| Opzione | Tipo | Predefinito | Descrizione |
|--------|------|---------|-------------|
| `output_format` | `String` | `"pdf"` | Formato di destinazione. |
| `save_to` | `String` | `nil` | Percorso locale su cui scaricare il risultato. |
| `output_filename` | `String` | `nil` | Sovrascrive il nome file generato. |
| `pdf_options` | `Hash` | `nil` | Impostazioni di pagina, rispettate quando l'output è PDF. |

### convert_to_markdown

Converte in Markdown pulito un file caricato di quasi qualsiasi formato documentale. Il formato viene rilevato sul server.

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

**Input accettati:** PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT e MD, oltre ai formati office legacy e ODF. Le immagini non sono supportate su questo endpoint.

L'output è un unico file `.md` strutturato per intestazioni, il che lo rende un buon mattone per l'ingestion RAG: un chunker semantico può suddividere sulla gerarchia di intestazioni del documento invece che su conteggi arbitrari di caratteri. Le uniche opzioni sono `save_to:` e `output_filename:`.

### convert_to_pdf

Converte in PDF un file caricato di quasi qualsiasi formato.

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

# pdf in passthrough, normalizzato in scala di grigi
client.convert_to_pdf("scan.pdf", pdf_options: { grayscale: true }, save_to: "gray.pdf")
```

**Input accettati:** office, ODF, Pages, Numbers, RTF, CSV, HTML, Markdown, testo semplice, immagini raster, SVG, EPUB, oppure un PDF esistente che viene passato in passthrough e normalizzato.

<div class="alert alert-warning">
<strong>Qui viene rispettato solo <code>pdf_options[:grayscale]</code>.</strong> Gli altri campi di geometria di pagina vengono ignorati su questo endpoint. Quando ti serve il controllo completo su dimensione pagina, orientamento e margini, usa <code>convert_document</code> oppure <code>convert_url_to_pdf</code>.
</div>

Le opzioni sono `save_to:`, `output_filename:` e `pdf_options:` (solo grayscale).

### convert_website_to_pdf e convert_website_to_screenshot

Individua ogni pagina di un sito web, converte ciascuna in background e raccoglie i risultati in un unico ZIP. Entrambi i metodi sono asincroni e restituiscono subito 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` funziona in modo identico e produce uno ZIP di PNG.

| Opzione | Tipo | Descrizione |
|--------|------|-------------|
| `crawl_mode` | `String` | Come vengono individuate le pagine, per esempio `"sitemap"`. |
| `include_patterns`, `exclude_patterns` | `Array` | Sottoponi al crawl, oppure salta, solo gli URL che corrispondono a questi pattern. |
| `notification_email` | `String` | Indirizzo email da avvisare quando il batch termina. |
| `callback_url` | `String` | Webhook chiamato al completamento. |
| `output_filename` | `String` | Sovrascrive il nome file ZIP generato. |
| `viewport_width`, `viewport_height`, `load_media`, `enable_scroll` | `Integer`, `Boolean` | Comportamento di rendering per pagina. Inviati solo quando li imposti, altrimenti il gateway applica i propri valori predefiniti. |
| `auth`, `cookies`, `headers` | `Hash`, `Array`, `Hash` | Accesso del browser alle pagine protette. |
| `single_page`, `pdf_options` | `Boolean`, `Hash` | Solo per i batch PDF. |

`wait_for_batch(batch_id, interval: 5, timeout: 1800, save_to: nil)` esegue il polling finché il batch non esce da `"processing"`, poi restituisce il `BatchStatus` finale. Con `save_to` scarica anche lo ZIP. Genera `Enconvert::APIError` con stato `504` quando viene raggiunto il timeout, e con stato `500` quando un batch termina senza uno ZIP da salvare.

### get_job_status

Interroga un singolo job di conversione tramite id.

```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
```

Restituisce un `JobStatus` con `status`, `presigned_url`, `object_key` ed `error`. Raramente devi chiamarlo tu stesso, perché l'SDK lo interroga già per tuo conto. Vedi [Recupero dei timeout](#recupero-dei-timeout).

### Conversioni supportate

La gem porta con sé la tabella completa degli endpoint `{input}-to-{output}` implementati e valida ogni coppia localmente, quindi una coppia non supportata genera `Enconvert::Error` prima che una richiesta lasci il tuo processo.

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

| Input | Output |
|-------|---------|
| `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` | tra loro, tutte le 20 coppie |
| `pdf` | `jpeg` |

Sono 43 endpoint: 13 di testo strutturato, 9 documentali e 21 di conversione immagini.

---

## Web intelligence (V2)

`client.v2` trasforma le pagine web live in dati pronti per gli agenti: rendering, enumerazione, ricerca, estrazione, ingestion e monitoraggio. Ogni rendering porta con sé `render_quality`, un punteggio da 0.0 a 1.0 associato a ogni lettura. Un punteggio basso significa che la pagina non è stata renderizzata in modo pulito, tipicamente una pagina di challenge, un cookie wall, un blocco di login o uno shell JavaScript vuoto. Il contenuto viene comunque restituito, ma segnalato, insieme a un array `warnings` e a un Hash `deductions` che dice quali controlli sono scattati. Leggi il punteggio prima di mettere il contenuto da qualche parte, e una lettura difettosa non entrerà mai silenziosamente nel contesto del tuo agente.

Tutti gli endpoint V2 richiedono una chiave API privata. Vedi la [panoramica V2](/it/docs/v2-overview) per il riferimento a livello di endpoint.

### Perceive

Esegue il rendering di un URL negli artefatti che richiedi. `perceive` è sincrono e restituisce l'operazione completata con gli URL degli artefatti appena firmati.

```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_...", poi da 0.0 a 1.0
puts op.outputs["markdown"].url                  # URL firmato dell'artefatto
puts op.outputs["markdown"].expires_in           # secondi prima che smetta di funzionare
puts op.structured, op.warnings.inspect          # Hash Ruby semplice, poi Array di String
```

Rifirma in qualsiasi momento gli URL degli artefatti di un'operazione precedente:

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

Puoi percepire fino a 1000 URL con un unico blocco di opzioni condiviso. I batch piccoli girano inline e tornano completati. Quelli più grandi tornano con stato `"queued"`, quindi interrogali tramite `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
```

Salta del tutto il round trip dell'URL firmato con `perceive_direct`, dove il corpo della risposta HTTP è l'artefatto stesso:

```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)

# Riscarica un artefatto archiviato di un'operazione precedente.
raw = client.v2.download_perceive_artifact(op.operation_id, output: "markdown")
```

`perceive_direct` richiede esattamente un output che produca artefatti e altrimenti genera `Enconvert::Error` localmente. `structured` può viaggiare insieme, perché resta inline lato server e non viene mai trasmesso in streaming. `download_perceive_artifact` prende un `output:` opzionale, che puoi omettere quando l'operazione ha prodotto esattamente un artefatto e devi indicare quando ne ha prodotti più di uno, e restituisce `410` quando un artefatto ha superato la sua finestra di conservazione.

| Opzione | Tipo | Descrizione |
|--------|------|-------------|
| `outputs` | `Array` | Uno o più tra `markdown`, `html_cleaned`, `html_raw`, `screenshot`, `screenshot_full_page`, `pdf`, `links`, `images`, `structured`. Lato server il valore predefinito è `["markdown", "structured"]`. |
| `extract` | `Array` | Uno o più tra `tables`, `prices`, `contacts`, `metadata`, `main_content`, `headings`, `structured_data`, `technologies`, `all`. |
| `schema` | `Hash` | Descrizioni dei campi per l'estrazione strutturata. |
| `wait_for`, `wait_timeout_ms` | `String`, `Integer` | Selettore CSS da attendere prima della cattura, e il limite su quell'attesa. |
| `js_code` | `String` | JavaScript da eseguire nella pagina prima della cattura. |
| `viewport`, `mobile` | `Hash`, `Boolean` | Dimensioni del viewport, e se renderizzare con un profilo mobile. |
| `headers`, `cookies`, `auth` | `Hash`, `Array`, `Hash` | Accesso del browser alle pagine protette. |
| `proxy_url` | `String` | Instrada il rendering attraverso il tuo proxy. |
| `geolocation` | `Hash` | Geolocalizzazione simulata per il contesto del browser. |
| `action_chain` | `Array` | Click, scroll e input programmati prima della cattura. |
| `cache_mode` | `String` | `enabled`, `bypass` oppure `refresh`. |
| `pdf_options` | `Hash` | Geometria di pagina quando `pdf` è tra gli output. |
| `block_resources` | `Array` | Tipi di risorsa da bloccare, per esempio `image`, `font`, `script`. |
| `respect_robots` | `Boolean` | Rispetta `robots.txt`. |
| `only_main_content` | `Boolean` | Rimuove navigazione ed elementi ripetuti dall'output Markdown. |
| `direct_download` | `Boolean` | Accettato solo da `perceive`. `perceive_batch` lo rifiuta. |

La semantica completa dei parametri si trova in [Perceive](/it/docs/v2-perceive) e in [Parametri e opzioni](/it/docs/parameters-options).

### Discover

Enumera gli URL di un sito senza renderizzare nulla. Non è coinvolto alcun browser, quindi è veloce ed economico.

```ruby
found = client.v2.discover(
  "https://example.com",
  mode: "hybrid",            # "sitemap", "crawl" oppure "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 quando max_urls ha limitato il risultato
puts found.sources.inspect  # da dove proviene ciascun URL
```

Vedi [Discover](/it/docs/v2-discover) per il comportamento modalità per modalità.

### Lookup

Esegue una ricerca web categorizzata e, facoltativamente, percepisce i risultati migliori nella stessa chiamata.

```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  # presente per i risultati percepiti automaticamente
end

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

Con `perceive_top: 3`, i primi tre URL dei risultati vengono renderizzati e portano inline un `PerceiveResult` completo su `hit.perceive`. I loro id di operazione sono raccolti anche in `search.perceive_operation_ids`. Altro in [Lookup](/it/docs/v2-lookup).

### Distill

Estrazione strutturata guidata da schema. Fornisci esattamente uno tra `urls:` e `discover_from:`, e `schema:` è sempre obbligatorio. Se sbagli, l'SDK genera `Enconvert::Error` localmente.

```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" oppure "none"
puts item.fields_from_css, item.fields_from_llm
```

Il `css_schema` opzionale viene eseguito per primo e risponde a tutto ciò che può con i soli selettori. Solo i campi che non riesce a coprire passano al livello del modello linguistico, ed è per questo che `fields_from_css` e `fields_from_llm` vengono riportati separatamente.

Individua ed estrai in un'unica chiamata:

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

I tipi di campo CSS sono `text`, `attribute`, `html`, `regex`, `nested`, `list` e `nested_list`. Gli elenchi di campi annidati vengono serializzati in modo ricorsivo. Vedi [Distill](/it/docs/v2-distill).

### Ingest

Trasforma un intero sito, o un insieme di documenti caricati, in JSONL suddiviso in chunk e pronto per il RAG attraverso un'unica pipeline. Ingest è sempre asincrono.

```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
```

La modalità `"urls"` prende un elenco `urls:` e rifiuta `url:`. Ogni altra modalità prende un `url:` seed e rifiuta `urls:`. Entrambe le regole vengono applicate localmente prima che la richiesta venga inviata.

Carica file invece di fare il crawl:

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

`ingest_files` accetta PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT e MD, oltre ai formati office legacy e ODF. Ogni voce può essere un percorso, un oggetto simile a un IO oppure un Hash `{ data:, filename: }`.

Gestisci i job e la consegna dei webhook:

```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)   # idempotente

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            # le vecchie firme smettono subito di essere valide
client.v2.retry_ingest_webhook(job.job_id) # rispedisce il webhook di un job completato
```

`retry_ingest_webhook` restituisce un `WebhookRetryResult` con `delivered`, `attempts`, `status_code` e `detail`. Risponde `409` quando il job non è completato e `400` quando il job non ha alcun webhook configurato. Vedi [Ingest](/it/docs/v2-ingest).

### Watch

Rifà il rendering di un URL a cadenza fissa e ti avvisa quando cambia.

```ruby
watcher = client.v2.create_watcher(
  "https://example.com/pricing",
  frequency_minutes: 60,     # minimo orario
  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: "")  # cancella il webhook
client.v2.delete_watcher(watcher.watcher_id)                   # soft delete, idempotente
```

`update_watcher` genera `Enconvert::Error` se lo chiami senza campi da aggiornare. `delete_watcher` restituisce il watcher marcato come eliminato con stato `"deleted"`, e un watcher eliminato viene riletto come `404`. Semantica completa in [Watch](/it/docs/v2-watch).

---

## Opzioni PDF

`pdf_options` è un semplice Hash con chiavi simbolo, accettato da `convert_url_to_pdf`, `convert_website_to_pdf`, `convert_document`, `convert_to_pdf` (solo grayscale) e `client.v2.perceive` quando `pdf` è tra gli output. Sul filo vengono inviate solo le chiavi che imposti.

```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"
)
```

| Chiave | Tipo | Descrizione |
|-----|------|-------------|
| `page_size` | `String` | `"A4"`, `"A3"`, `"Letter"`, `"Legal"` e simili. |
| `page_width` | `Number` | Larghezza di pagina esplicita, in alternativa a `page_size`. |
| `page_height` | `Number` | Altezza di pagina esplicita. |
| `orientation` | `String` | `"portrait"` oppure `"landscape"`. |
| `margins` | `Hash` | `{ top:, bottom:, left:, right: }`, tutti opzionali. |
| `scale` | `Number` | Scala di rendering, per esempio `0.9` per il 90 percento. |
| `grayscale` | `Boolean` | Post-elabora il PDF in scala di grigi. |
| `header` | `Hash` | Testo dell'intestazione per regione di pagina. |
| `footer` | `Hash` | Testo del piè di pagina per regione di pagina. |

---

## Gestione degli errori

Ogni errore è un `Enconvert::Error` oppure una delle sue sottoclassi, quindi le clausole `rescue` possono essere ampie o strette quanto vuoi. Gli errori di validazione lato client, come una coppia di conversione non supportata o uno `schema` mancante, generano la classe base `Enconvert::Error` prima che venga effettuata qualsiasi richiesta HTTP.

```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 | Generata per | Codice di stato |
|-------|-----------|-------------|
| `Enconvert::AuthenticationError` | Chiave API non valida, mancante o revocata | `401`, `403` |
| `Enconvert::QuotaError` | Generata su HTTP 402 | `402` |
| `Enconvert::RateLimitError` | Troppe richieste | `429` |
| `Enconvert::APIError` | Qualsiasi altra risposta da 400 in su | il codice effettivo |
| `Enconvert::Error` | Classe base, più tutti gli errori di validazione locali | nessuno |

`APIError#status_code` ti dà lo stato HTTP, e il messaggio viene preso dal campo `detail` oppure `error` del corpo della risposta quando il corpo è JSON. La mappa completa dei messaggi si trova in [Codici di errore](/it/docs/error-codes).

---

## Recupero dei timeout

Un rendering lungo da URL a PDF o la conversione di un documento di grandi dimensioni possono superare il timeout del reverse proxy anche quando la conversione stessa riesce sul server. L'SDK recupera la situazione in modo trasparente, senza codice da parte tua.

1. Prima di ogni richiesta di conversione il client genera un job id di 32 caratteri e lo invia come `job_id` nel corpo o nel form multipart.
2. Se la richiesta torna con uno stato pari o superiore a 500, il client passa a interrogare `GET /v1/convert/status/{job_id}` ogni 3 secondi. Un `404` mentre la riga del job è ancora in scrittura viene ignorato e il polling continua.
3. Non appena il job risulta `success`, il risultato viene restituito. Non appena risulta `failed`, viene generato `Enconvert::APIError` con stato `500` e il messaggio di errore del server.
4. Il limite di tempo per il polling è di 300 secondi. Superato quello, il client genera `Enconvert::APIError` con stato `504` e il messaggio `Conversion timed out`.

Il `job_id` generato dal client viene anche unito a ogni risposta riuscita, quindi `result.job_id` è sempre popolato e più tardi puoi interrogare `get_job_status` tu stesso, se vuoi.

<div class="alert alert-info">
<strong>Gli invii batch dei siti web sono esclusi.</strong> <code>convert_website_to_pdf</code> e <code>convert_website_to_screenshot</code> non creano una riga per singolo job, quindi lì un 5xx significa che l'invio stesso è fallito e viene riportato subito invece di essere interrogato.
</div>

---

## Configurazione

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

| Opzione | Tipo | Predefinito | Descrizione |
|--------|------|---------|-------------|
| `api_key` | `String` | obbligatorio | La tua chiave API privata. Un valore `nil` o vuoto genera `Enconvert::Error` alla costruzione. |
| `timeout` | `Integer` | `300` | Timeout di apertura e lettura in **secondi**, applicato a ogni richiesta. |
| `base_url` | `String` | `"https://api.enconvert.com"` | URL base dell'API. Gli slash finali vengono rimossi. Sovrascrivilo per un gateway self-hosted. |

Le richieste si autenticano con l'header `X-API-Key`. Quando passi `save_to`, il download aggira deliberatamente quell'header, perché l'URL presigned dello storage non deve ricevere la tua chiave API.

<div class="alert alert-warning">
<strong>Non inserire mai la chiave API direttamente nel codice.</strong> Leggila da una variabile d'ambiente, dalle credenziali di Rails o dal tuo secret manager. Chiunque abbia la tua chiave privata può eseguire conversioni a tuo nome. Ruotala dalla <a href="/it/dashboard">dashboard</a> se trapela.
</div>

---

## Struttura del risultato

Ogni conversione di un singolo file e di una singola pagina restituisce un `Enconvert::ConversionResult`:

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

result.presigned_url            # URL firmato per l'output convertito
result.object_key               # chiave dell'oggetto nello storage
result.filename                 # nome file lato server
result.file_size                # byte, oppure nil
result.conversion_time_seconds  # secondi, oppure nil
result.job_id                   # sempre popolato dal client
```

Le altre Struct V1 sono `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`) e `BatchItem` (`source_url`, `status`, `download_url`, `output_file_size`, `duration`).

Sul lato V2, `PerceiveResult` è quella da conoscere:

```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              # da 0.0 a 1.0 (oppure nil), e se proviene dalla cache
op.outputs                                   # Hash di nome => V2OutputArtifact
op.structured, op.extraction_tier            # Hash (oppure nil), e come è stato estratto
op.tokens.input, op.tokens.output, op.cost_cents, op.duration_ms
op.error, op.warnings                        # String oppure nil, e Array di String
op.status_code                               # stato HTTP a monte della pagina
op.deductions, op.options_echo               # perché render_quality è calato, e le opzioni usate
```

Un `V2OutputArtifact` porta `url`, `object_key`, `size_bytes`, `content_type` ed `expires_in` (900 secondi per impostazione predefinita), quindi gli URL firmati degli artefatti hanno vita breve. Rifirmali con `get_perceive_operation`, oppure scarica i byte con `download_perceive_artifact`. I payload forniti dall'utente, come schemi, dati estratti, campi monitorati e voci di diff, passano intatti come semplici Hash Ruby.

Anche gli URL presigned delle conversioni V1 sono temporanei. Passa `save_to` quando vuoi i byte su disco, oppure copiali nel tuo bucket per un'archiviazione permanente.

---

## Sorgente e problemi

- **RubyGems:** [enconvert](https://rubygems.org/gems/enconvert)
- **GitHub:** [conversionapi/ruby-sdk](https://github.com/conversionapi/ruby-sdk)
- **Ruby:** 3.0.0 o versioni successive, nessuna dipendenza a runtime
- **Licenza:** MIT
- **Altri linguaggi:** [Tutti gli SDK](/it/docs/sdks)
- **Riferimento API:** [Panoramica degli endpoint](/it/docs/endpoints-overview), [Autenticazione](/it/docs/authentication)

---

## Domande frequenti

### Come converto i file in Ruby con una gem?

Esegui `gem install enconvert`, costruisci un client con `Enconvert::Client.new(api_key: ENV.fetch("ENCONVERT_API_KEY"))` e chiama un metodo come `convert_document`, `convert_image` oppure `convert_url_to_pdf`. Passa `save_to:` e l'SDK scarica per te il risultato su quel percorso, creando le directory padre se servono.

### Come converto DOCX in PDF in Ruby?

`client.convert_document("report.docx", save_to: "report.pdf")`. Il formato di output predefinito è `"pdf"`, quindi ti serve `output_format:` solo quando vuoi qualcos'altro. La stessa chiamata funziona per `.doc`, `.xls`, `.xlsx`, `.ppt`, `.pptx`, `.odt`, `.ods`, `.odp`, `.ots`, `.pages` e `.numbers`.

### Come converto un URL in PDF in Ruby?

`client.convert_url_to_pdf("https://example.com", save_to: "page.pdf")`. Imposta `single_page: false` per paginare invece di produrre una singola pagina continua, e passa `pdf_options:` per dimensione pagina, orientamento, margini e scala. Per una pagina protetta da HTTP basic auth, aggiungi `auth: { username: ..., password: ... }`.

### Come converto HEIC in WebP in Ruby?

`client.convert_image("photo.heic", output_format: "webp", save_to: "photo.webp")`. Il formato di input viene ricavato dall'estensione del nome file, e la gem converte liberamente tra `jpeg`, `png`, `svg`, `heic` e `webp`. Rasterizza anche un PDF in JPEG con `output_format: "jpeg"`.

### Come estraggo una pagina web in Markdown da Ruby?

Ci sono due opzioni. `client.convert_url_to_markdown(url, save_to: "article.md")` ti dà Markdown pulito in stile GitHub-Flavored con frontmatter YAML. `client.v2.perceive(url, outputs: %w[markdown])` ti dà lo stesso contenuto più un punteggio `render_quality`, gli avvisi e l'estrazione strutturata opzionale, che è ciò che ti serve quando a leggere il risultato sarà un agente.

### Che cosa significa render_quality e perché dovrei controllarlo?

È un punteggio da 0.0 a 1.0 associato a ogni lettura V2, che riflette quanto pulitamente la pagina è stata effettivamente renderizzata. Una pagina di challenge, un cookie wall, un blocco di login, una pagina di errore HTTP o uno shell JavaScript vuoto ottengono tutti un punteggio basso. Il contenuto viene comunque restituito così puoi ispezionarlo, con `warnings` e `deductions` a spiegare il punteggio. Usalo come filtro prima di archiviare il testo o darlo in pasto a un modello.

### L'SDK Ruby riprova quando una conversione lunga va in timeout?

Sì. Ogni richiesta di conversione porta con sé un `job_id` generato dal client e, se la richiesta fallisce con uno stato pari o superiore a 500, l'SDK interroga `GET /v1/convert/status/{job_id}` ogni 3 secondi per un massimo di 300 secondi. Restituisce il risultato una volta che il job riesce e genera `Enconvert::APIError` con stato `504` se il limite scade. Gli invii batch di interi siti sono esclusi, perché non hanno una riga per singolo job.

### Che cosa succede se chiedo una coppia di conversione che l'API non implementa?

La gem genera `Enconvert::Error` localmente, prima di qualsiasi chiamata di rete, e il messaggio elenca gli output validi per quell'input. Puoi controllare tu stesso la stessa tabella con `Enconvert.valid_outputs_for("json")`, che restituisce `["csv", "toml", "xml", "yaml"]`.

### Posso usare la gem enconvert dentro Rails o in un background job?

Sì, e un background job è il posto giusto per usarla. La gem è puro `net/http` senza dipendenze a runtime e senza stato globale, quindi un'istanza di `Enconvert::Client` si può costruire per singolo job oppure memoizzare per processo. Le conversioni lunghe bloccano il thread chiamante fino al `timeout` configurato, che vale 300 secondi per impostazione predefinita, quindi tienile fuori dal ciclo di una richiesta web.

### Quali versioni di Ruby supporta l'SDK?

Ruby 3.0.0 e versioni successive, come dichiarato da `required_ruby_version` nella gemspec. Non ci sono dipendenze gem a runtime, quindi si installa in qualsiasi gruppo Bundler senza trascinarsi dietro un albero di dipendenze.

### Come verifico la firma di un webhook di ingest?

Chiama `client.v2.get_webhook_secret`, che crea il secret di firma del progetto al primo utilizzo e lo restituisce insieme a `signature_header`, `timestamp_header`, `signature_scheme` e `replay_tolerance_seconds`. Calcola l'HMAC sul corpo consegnato e confrontalo con l'header della firma. Usa `rotate_webhook_secret` per invalidare il vecchio secret, e `retry_ingest_webhook(job_id)` per rispedire la notifica di un job completato.
