---
seo_title: Autenticazione API Key: X-API-Key e JWT | EnConvert
meta_desc: EnConvert ha due tipi di chiave: private sk_ per i server, pubbliche pk_ scambiate per un JWT nel browser, più GET /v1/auth/verify per controllarle.
keywords: autenticazione api key header x-api-key, token bearer jwt autenticazione api, differenza chiavi sk_ e pk_, scambiare chiave pubblica per token jwt, endpoint /v1/auth/token, verificare se una chiave api è valida, ruotare chiave api senza downtime, whitelist domini chiave api, errore 403 forbidden allowed_endpoints, autenticazione api lato client chiave pubblica
---

# Autenticazione

EnConvert ha due tipi di chiave API, e il posto in cui gira il tuo codice decide quale usare. Una chiave privata (`sk_`) va nell'header `X-API-Key` da un server che controlli tu; una chiave pubblica (`pk_`) viene scambiata con un JWT a breve durata che il codice del browser invia come `Authorization: Bearer <token>`.

---

## Scegliere il tipo di chiave

| | Chiave privata | Chiave pubblica + JWT |
|---|---|---|
| Prefisso | `sk_` | `pk_` |
| Inviata come | `X-API-Key: sk_your_private_key` | `Authorization: Bearer <token>` |
| Gira in | server, script, job CI, container | browser, widget incorporati, qualsiasi cosa distribuita a un client |
| Rifiutata quando | la richiesta porta un header `Origin` (`403`) | chiama qualcosa di diverso da `/v1/auth/token` o `/v1/auth/branding` (`403`) |
| Copertura | ogni endpoint: sincrono, asincrono, batch, webhook | un elemento per richiesta, sincrono, download presigned |
| Ambito | `allowed_endpoints` opzionale | `allowed_domains` più `allowed_endpoints` |
| Durata | chiave valida fino alla revoca | chiave valida fino alla revoca, token di accesso 1 ora, cookie di refresh 7 giorni |

Scegli in base al target di deploy. Un servizio backend, uno script, un cron job o uno strumento interno prende una chiave privata. Un'app basata su browser o un widget incorporato prende una chiave pubblica con JWT. Non c'è modo di nascondere una chiave privata nel codice frontend: il gateway la rifiuta già solo per la presenza di un header `Origin`, prima di controllare qualsiasi altra cosa.

Una chiave è il suo prefisso seguito da un token casuale, 46 caratteri in tutto. I nomi segnaposto negli esempi qui sotto (`sk_your_private_key`, `pk_your_public_key`) stanno al posto di quel token; una chiave reale non contiene alcun segmento di ambiente come `live` o `test`. Qualsiasi cosa più corta di 45 caratteri viene rifiutata con `401 Invalid API Key format` prima ancora che il prefisso venga letto.

Sul server viene conservato solo un hash SHA-256 di ogni chiave, insieme a uno stub di prefisso di sette caratteri, così puoi distinguere le tue chiavi nella dashboard.

---

## Chiavi private {: #private-keys }

Le chiavi private sono pensate per le **applicazioni lato server**, dove la tua chiave API può essere mantenuta segreta. Forniscono accesso completo a tutti gli endpoint e a tutte le funzionalità dell'API.

- **Header:** `X-API-Key: sk_your_private_key`
- **Accesso:** Accesso completo a tutti gli endpoint, comprese le operazioni sincrone e asincrone, l'elaborazione batch e tutti i tipi di conversione.
- **Sicurezza:** Le chiavi vengono memorizzate come hash SHA-256 sul server. La chiave in chiaro viene mostrata una sola volta, al momento della creazione.

Non è richiesto alcuno scambio di token né gestione di sessioni. Includi la chiave in ogni richiesta:

```bash
curl -X POST https://api.enconvert.com/v1/convert/url-to-pdf \
  -H "X-API-Key: sk_your_private_key" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com"}'
```

### Formato dell'header

Includi la tua chiave privata nell'header `X-API-Key` a ogni richiesta:

```http
X-API-Key: sk_your_private_key
```

Le chiavi private iniziano sempre con il prefisso `sk_`. Puoi generare e gestire le tue chiavi dalla dashboard di EnConvert.

### Esempio: conversione di file

Converti un file JSON in XML usando una chiave privata:

```bash
curl -X POST https://api.enconvert.com/v1/convert/json-to-xml \
  -H "X-API-Key: sk_your_private_key" \
  -F "file=@data.json"
```

Risposta:

```json
{
  "presigned_url": "https://econverter.nyc3.cdn.digitaloceanspaces.com/...",
  "object_key": "live/files/12345/json-to-xml/data_20250202_120530123.xml",
  "filename": "data_20250202_120530123.xml",
  "file_size": 1024,
  "conversion_time_seconds": 0.45
}
```

- `presigned_url`: un URL temporaneo e scaricabile per recuperare il file convertito.
- `object_key`: il percorso di archiviazione del file convertito (ad es., `live/files/12345/json-to-xml/...`). Non è un URL.
- `filename`: il nome file generato per il file convertito.
- `file_size`: la dimensione del file di output in byte.
- `conversion_time_seconds`: il tempo impiegato per completare la conversione.

Quegli URL di download hanno vita breve. Le regole di scadenza e conservazione sono su [URL firmati](/it/docs/concepts/signed-urls.md).

### Restrizioni sugli endpoint

Per impostazione predefinita, una chiave privata ha accesso a tutti gli endpoint dell'API. Puoi facoltativamente limitare una chiave a endpoint specifici usando l'impostazione **allowed_endpoints** al momento della creazione.

Quando `allowed_endpoints` è configurato, la chiave potrà chiamare solo gli endpoint elencati. Le richieste verso qualsiasi altro endpoint verranno rifiutate con un errore `403 Forbidden`: `Endpoint '{path}' not allowed for this API key`.

Esempio di configurazione:

```json
{
  "allowed_endpoints": [
    "/v1/convert/url-to-pdf",
    "/v1/convert/json-to-xml",
    "/v1/convert/html-to-pdf"
  ]
}
```

Questo è utile quando vuoi emettere una chiave con ambito limitato, ad esempio una chiave che può eseguire solo conversioni PDF.

Un piccolo gruppo di percorsi resta raggiungibile qualunque cosa dica l'elenco, perché una chiave che può avviare un job deve poterlo anche portare a termine:

- `/v1/auth/token`, `/v1/auth/verify` e `/v1/whoami`
- `/v1/convert/status/{job_id}`, `/v1/convert/batch/{batch_id}` e `/v1/convert/download/{object_key}`
- `/v1/extension/*`, per le richieste autenticate con JWT
- i percorsi V2 per singolo job di perceive, ingest e watch

Una chiave creata con la singola voce `["*"]` indica ogni endpoint, compresi quelli rilasciati dopo la creazione della chiave.

L'elenco è fissato al momento della creazione. Non esiste una chiamata che modifichi le restrizioni di una chiave esistente, quindi restringere o allargare l'ambito di una chiave significa crearne una nuova e revocare la vecchia. Vedi [Mantenere le chiavi al sicuro](#keeping-keys-safe).

<div class="alert alert-warning">
<strong>Non usare chiavi private nel codice lato client.</strong> L'API rileva l'header <code>Origin</code> inviato dai browser e rifiuterà le richieste effettuate con una chiave privata da un ambiente browser con <code>403 Private API keys cannot be used from browsers</code>. Per le integrazioni lato client, usa invece una <a href="#public-keys-and-jwt">chiave pubblica con JWT</a>.
</div>

---

## Chiavi pubbliche e JWT {: #public-keys-and-jwt }

L'autenticazione con chiave pubblica JWT permette alle app lato client (browser) di chiamare l'API EnConvert: scambi la tua chiave pubblica (`pk_`) con un token di accesso JWT a breve durata tramite `POST /v1/auth/token`, poi invii quel token nell'header `Authorization: Bearer <token>` sulle richieste API. Poiché una chiave pubblica è visibile agli utenti finali, non può chiamare l'API direttamente. Da sola raggiunge esattamente due percorsi, `/v1/auth/token` e `/v1/auth/branding`. Tutto il resto restituisce `403` con un messaggio che ti dice di scambiare prima la chiave con un token.

1. **Scambia** la tua chiave pubblica (`pk_`) con un token di accesso JWT chiamando `POST /v1/auth/token`.
2. **Usa** il token JWT nell'header `Authorization: Bearer <token>` sulle richieste API.
3. **Aggiorna** il token automaticamente prima della scadenza usando `POST /v1/auth/refresh`.
4. **Il whitelisting dei domini** garantisce che vengano accettate solo le richieste provenienti dai tuoi domini approvati.

### Passo 1: scambia la chiave pubblica con un JWT

```http
POST /v1/auth/token
```

| Header | Valore | Descrizione |
|---|---|---|
| `X-API-Key` | `pk_your_public_key` | La tua chiave API pubblica |

L'endpoint si aspetta un oggetto JSON come corpo. Non inviare alcun corpo restituisce `422` con `{"type":"missing","loc":["body"],"msg":"Field required"}`, quindi invia `{}` quando non hai nulla da passare. L'unico campo facoltativo è `turnstile_token`, che viene verificato solo per le richieste provenienti dall'origine del widget di EnConvert e ignorato ovunque altro.

```javascript
async function getToken() {
  const response = await fetch("https://api.enconvert.com/v1/auth/token", {
    method: "POST",
    headers: {
      "X-API-Key": "pk_your_public_key",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({}),
    credentials: "include",
  });

  if (!response.ok) {
    throw new Error(`Token exchange failed: ${response.status}`);
  }

  const data = await response.json();
  return data.token;
}
```

<div class="alert alert-warning">
<strong>Importante:</strong> Devi includere <code>credentials: "include"</code> nelle opzioni di fetch. Questo garantisce che il cookie del refresh token venga memorizzato dal browser, condizione necessaria per l'aggiornamento automatico del token.
</div>

Risposta:

```json
{
  "token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600
}
```

La risposta imposta anche un cookie `HttpOnly` contenente il refresh token. Questo cookie è gestito automaticamente dal browser ed è usato durante l'aggiornamento del token di accesso.

Una chiave privata inviata a questo endpoint viene rifiutata con `400 Only public API keys can exchange for tokens. Private keys should be used directly.` È l'API che ti dice di saltare il passo dello scambio, non una chiave difettosa.

### Passo 2: usa il token JWT

Includi il token JWT nell'header `Authorization` come token Bearer su tutte le richieste API successive.

```javascript
async function convertUrlToPdf(token, url) {
  const response = await fetch("https://api.enconvert.com/v1/convert/url-to-pdf", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${token}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ url }),
  });

  return await response.json();
}

// Usage
const token = await getToken();
const result = await convertUrlToPdf(token, "https://example.com");
console.log(result.presigned_url);
```

### Passo 3: aggiornamento automatico del token

I token di accesso scadono dopo un'ora. Usa l'endpoint di refresh per ottenere un nuovo token di accesso senza richiedere all'utente di riautenticarsi.

```http
POST /v1/auth/refresh
```

Il refresh token viene inviato automaticamente tramite il cookie `HttpOnly` impostato durante lo scambio iniziale del token. Non sono necessari corpo della richiesta né header aggiuntivi.

```javascript
class EnconvertClient {
  constructor(publicKey) {
    this.publicKey = publicKey;
    this.token = null;
    this.tokenExpiry = null;
  }

  async getToken() {
    const response = await fetch("https://api.enconvert.com/v1/auth/token", {
      method: "POST",
      headers: {
        "X-API-Key": this.publicKey,
        "X-Parent-Origin": window.location.origin,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({}),
      credentials: "include",
    });

    const data = await response.json();
    this.token = data.token;
    // Set expiry to 55 minutes (refresh before the 1-hour expiry)
    this.tokenExpiry = Date.now() + 55 * 60 * 1000;
    return this.token;
  }

  async refreshToken() {
    const response = await fetch("https://api.enconvert.com/v1/auth/refresh", {
      method: "POST",
      credentials: "include",
    });

    if (!response.ok) {
      // Refresh token expired, re-authenticate
      return await this.getToken();
    }

    const data = await response.json();
    this.token = data.token;
    this.tokenExpiry = Date.now() + 55 * 60 * 1000;
    return this.token;
  }

  async getValidToken() {
    if (!this.token || Date.now() >= this.tokenExpiry) {
      if (this.token) {
        return await this.refreshToken();
      }
      return await this.getToken();
    }
    return this.token;
  }

  async convert(endpoint, body) {
    const token = await this.getValidToken();
    const response = await fetch(`https://api.enconvert.com${endpoint}`, {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${token}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(body),
    });

    return await response.json();
  }
}

// Usage
const client = new EnconvertClient("pk_your_public_key");
const result = await client.convert("/v1/convert/url-to-pdf", {
  url: "https://example.com",
});
```

Quattro cose da sapere prima di metterti a debuggare un refresh che fallisce:

- Il refresh risolve il tuo progetto a partire dal cookie, poi cerca una qualsiasi chiave pubblica attiva su quel progetto. Se revochi tutte le chiavi pubbliche, il refresh inizia a restituire `401`, anche mentre il cookie è ancora dentro i suoi sette giorni.
- La route di refresh non ricontrolla la whitelist dei domini. Quel controllo avviene quando il token viene emesso.
- Il refresh non rilega il nuovo token alla chiave che avevi usato in origine. Se il progetto contiene più chiavi pubbliche, il token aggiornato può tornare indietro con le restrizioni di un'altra chiave.
- L'emissione e il refresh dei token hanno un proprio limite per IP, separato dai [rate limit](/it/docs/reference/rate-limits.md) del tuo piano. Un client bloccato in un loop di refresh se ne accorgerà.

### Durata dei token

| Token | Durata | Archiviazione |
|---|---|---|
| Token di accesso | 1 ora | Restituito nel corpo della risposta JSON; conservalo in memoria |
| Refresh token | 7 giorni | Impostato come cookie `HttpOnly`; gestito dal browser |

### Whitelisting dei domini

Le chiavi pubbliche sono limitate a domini specifici configurati nella tua dashboard.

La corrispondenza confronta solo host e porta. Lo schema viene prima rimosso da entrambi i lati, quindi `https://example.com` e `http://example.com` sono la stessa origine per quanto riguarda la whitelist. La porta invece non viene rimossa e fa parte della corrispondenza.

- **Corrispondenza esatta:** `https://example.com` corrisponde all'host nudo `example.com` su qualsiasi schema.
- **Sottodomini con wildcard:** `https://*.example.com` corrisponde a `https://app.example.com`, `https://staging.example.com` e anche all'apex `https://example.com`.
- **Specifica della porta:** `http://localhost:3000` corrisponde solo a quell'host e a quella porta.

| Voce in whitelist | Corrisponde | Non corrisponde |
|---|---|---|
| `https://example.com` | `https://example.com`, `http://example.com` | `https://www.example.com` |
| `https://*.example.com` | `https://app.example.com`, `https://dev.example.com`, `https://example.com` | `https://example.net` |
| `http://localhost:3000` | `http://localhost:3000` | `http://localhost:8080` |

Una richiesta da un'origine che non è nell'elenco riceve `403 Domain {origin} not authorized`, e il proprietario del progetto ne viene informato via email (al massimo una volta per chiave ogni 24 ore). Se la tua casella si sta riempiendo, la causa abituale è una voce obsoleta nella whitelist.

Due origini saltano del tutto il controllo del dominio: un'origine `chrome-extension://...`, così le estensioni del browser possono chiamare l'API, e l'origine del widget di EnConvert, dove il controllo si sposta invece sulla validazione dell'`X-Parent-Origin` del widget.

### Funzionalità di sicurezza

- **Token a breve durata:** I token di accesso scadono dopo 1 ora, limitando la finestra di esposizione in caso di compromissione di un token.
- **Cookie di refresh HttpOnly:** I refresh token sono archiviati in cookie `HttpOnly`, il che li rende inaccessibili a JavaScript e resistenti agli attacchi XSS.
- **Restrizioni di dominio:** I token vengono emessi solo quando la richiesta proviene da un dominio in whitelist.
- **Nessun accesso diretto all'API:** Le chiavi pubbliche da sole non possono chiamare gli endpoint di conversione. È sempre richiesto un JWT valido.

### Restrizioni delle chiavi pubbliche

L'autenticazione con chiave pubblica presenta le seguenti limitazioni rispetto alle chiavi private:

- **Solo sincrono:** Sono disponibili solo gli endpoint di conversione sincroni. La modalità asincrona e le callback webhook no; `notification_email` e `callback_url` vengono azzerati sulle conversioni con chiave browser.
- **Un solo elemento per richiesta:** Ogni richiesta può convertire un solo URL o file. L'invio di un array restituisce `400 Public keys only support a single URL input`.
- **Download diretto:** Le risposte forniscono un `presigned_url` per il download immediato. Non è disponibile alcuna opzione per destinazioni di archiviazione personalizzate.
- **Stato dei job sì, stato dei batch no:** `GET /v1/convert/status/{job_id}` funziona con un token browser, ed è così che il widget recupera un risultato dopo una connessione caduta. `GET /v1/convert/batch/{batch_id}` viene rifiutato con `403 Batch status requires a private API key`.

L'invio di batch in sé è limitato dal limite batch del tuo piano più che dal tipo di chiave, ma poiché una chiave browser è vincolata a un elemento per richiesta, in pratica i batch richiedono una chiave privata. Vedi [Elaborazione batch](/it/docs/guides/batch-processing.md).

<div class="alert alert-warning">
<strong>Best practice:</strong>
<ul>
  <li>Conserva sempre i token di accesso solo in memoria. Non persisterli mai in <code>localStorage</code> o <code>sessionStorage</code>.</li>
  <li>Implementa l'aggiornamento automatico del token per evitare interruzioni durante le sessioni utente.</li>
  <li>Mantieni l'elenco dei domini in whitelist il più specifico possibile. Evita wildcard troppo ampie.</li>
  <li>Usa <code>credentials: "include"</code> su tutte le richieste fetch per garantire che i cookie vengano inviati e ricevuti correttamente.</li>
  <li>Gestisci i fallimenti dell'aggiornamento del token in modo controllato, ricorrendo a una riautenticazione completa con la chiave pubblica.</li>
</ul>
</div>

Se vuoi il flusso browser senza scriverne una riga, il widget incorporabile emette e aggiorna i propri token da solo. Vedi [Widget web](/it/docs/guides/integrations.md#web-widgets).

---

## Verifica delle tue credenziali {: #verify-your-credentials }

`GET /v1/auth/verify` controlla se la tua autenticazione corrente è valida e riporta che cosa ne pensa l'API. Funziona con le chiavi private inviate nell'header `X-API-Key` e con i token bearer JWT inviati nell'header `Authorization`. Una richiesta valida restituisce il tuo `project_id`, `tier`, `key_type` e qualsiasi restrizione di dominio o endpoint; una chiave o un token non valido o scaduto restituisce `401 Unauthorized`.

```http
GET /v1/auth/verify
```

| Header | Valore | Descrizione |
|---|---|---|
| `X-API-Key` | `sk_your_private_key` | Autentica con una chiave privata |
| `Authorization` | `Bearer <token>` | Autentica con un token JWT |

Usa uno dei due header sopra indicati, non entrambi.

<div class="alert alert-warning">
<strong>Non puoi verificare direttamente una chiave pubblica.</strong> Inviare <code>X-API-Key: pk_...</code> a questo endpoint restituisce <code>403</code>, perché una chiave pubblica può chiamare solo <code>/v1/auth/token</code> e <code>/v1/auth/branding</code>. Emetti prima un token, poi verifica il token. Questo è l'unico caso in cui un <code>403</code> qui non significa che la tua chiave sia difettosa.
</div>

Con una chiave privata:

```bash
curl https://api.enconvert.com/v1/auth/verify \
  -H "X-API-Key: sk_your_private_key"
```

Con un token JWT:

```bash
curl https://api.enconvert.com/v1/auth/verify \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
```

Risposta:

```json
{
  "authenticated": true,
  "project_id": "12345",
  "tier": "pro",
  "key_type": "public",
  "allowed_domains": ["https://example.com", "https://*.example.com"],
  "allowed_endpoints": ["/v1/convert/url-to-pdf", "/v1/convert/jpeg-to-png"]
}
```

| Campo | Tipo | Descrizione |
|---|---|---|
| `authenticated` | boolean | Sempre `true` per una richiesta valida |
| `project_id` | string | L'ID del tuo progetto |
| `tier` | string | Il tuo piano di abbonamento (ad es., `free`, `starter`, `pro`, `business`) |
| `key_type` | string | `private`, `public` o `dashboard` |
| `allowed_domains` | array o null | Domini in whitelist (solo chiavi pubbliche, `null` negli altri casi) |
| `allowed_endpoints` | array o null | Endpoint con restrizioni (solo chiavi pubbliche, `null` negli altri casi) |

Due dettagli che spesso confondono. `key_type` ha un terzo valore, `dashboard`, che è quello emesso dal backend per una sessione autenticata della dashboard o del playground; come una chiave privata, riporta entrambi gli elenchi a `null`. E `tier` è lo slug del piano, non il nome che compare sulla pagina dei prezzi: un abbonamento Studio riporta `"tier": "pro"`. Gli slug `free`, `starter`, `pro`, `business` ed `enterprise` corrispondono a Founding, Indie, Studio, Production ed Enterprise.

Se la chiave o il token non è valido o è scaduto, l'API restituisce invece un errore `401 Unauthorized`. L'elenco completo dei messaggi di errore di autenticazione è in [Errori](/it/docs/reference/errors.md).

### GET /v1/whoami

C'è un secondo endpoint di identità, più piccolo. Richiede una chiave privata:

```bash
curl https://api.enconvert.com/v1/whoami \
  -H "X-API-Key: sk_your_private_key"
```

```json
{
  "project_id": "12345",
  "plan_slug": "pro"
}
```

Non restituisce nient'altro, ed è voluto: niente tipo di chiave, niente domini, niente limiti. Un JWT o una chiave pubblica ricevono `403 GET /v1/whoami requires a private API key (sk_...).` Diverse integrazioni lo usano come test delle credenziali, compreso il nodo n8n.

### Casi d'uso

- **Test delle chiavi API:** Conferma che una chiave appena creata sia attiva e configurata correttamente.
- **Controllo delle restrizioni di dominio:** Verifica quali domini sono in whitelist per una chiave pubblica.
- **Debug dei problemi di autenticazione:** Determina se il fallimento di una richiesta è causato dall'autenticazione o da qualcos'altro.

---

## Mantenere le chiavi al sicuro {: #keeping-keys-safe }

- **Archiviazione con hash:** Le chiavi private sono archiviate sul server come hash SHA-256. La chiave in chiaro viene mostrata una sola volta, al momento della creazione. Se la perdi, devi generare una nuova chiave.
- **Variabili d'ambiente:** Conserva la tua chiave in una variabile d'ambiente (ad es., `ENCONVERT_API_KEY`) invece di scriverla direttamente nel codice sorgente.
- **Ambito alla creazione:** `allowed_endpoints` e `allowed_domains` vengono impostati alla creazione della chiave e non possono essere modificati in seguito. Decidi l'ambito prima di premere il pulsante di creazione.

### Ruotare una chiave

La rotazione è crea-poi-revoca, e in questo ordine non costa alcun downtime:

1. Crea la nuova chiave nella dashboard con l'ambito che vuoi.
2. Distribuiscila, poi conferma che la nuova chiave è attiva con `GET /v1/auth/verify`.
3. Revoca la vecchia chiave.

Entrambe le chiavi funzionano durante il passo 2, quindi non esiste una finestra in cui il tuo servizio resti senza autenticazione. Poiché le restrizioni sono immutabili, cambiare l'ambito di una chiave è la stessa procedura della rotazione.

### Se una chiave viene esposta

Revocala per prima cosa, poi calcola il raggio d'azione. La revoca è l'unico interruttore di emergenza, perché l'ambito di una chiave attiva non può essere ristretto; una chiave revocata viene rifiutata con `401 API Key revoked`.

- Una **chiave privata** esposta può chiamare ogni endpoint per cui la chiave era abilitata e consuma le tue operazioni mensili. Revocala, creane una sostitutiva e controlla nella dashboard il tuo utilizzo per individuare chiamate che non hai fatto tu.
- Una **chiave pubblica** esposta è meno urgente, e questo è voluto per come è progettata. Non può chiamare affatto gli endpoint di conversione, ed emette token solo per le origini presenti nella sua whitelist. Restringere quella whitelist significa creare una chiave più stretta e revocare quella esposta, dato che l'elenco su una chiave esistente non può essere modificato.
- Un **token di accesso** esposto muore entro un'ora e non può essere usato da un'origine diversa da quella per cui è stato emesso. Il problema più duraturo è il suo cookie di refresh: il refresh riesce finché il progetto ha una qualsiasi chiave pubblica attiva, quindi revocare la chiave da cui proveniva il token non uccide il cookie a meno che non fosse la tua ultima chiave pubblica.

<div class="alert alert-warning">
<strong>Una chiave finita su git è già pubblica.</strong> Revocala nella dashboard prima di riscrivere la storia del repository. Cancellare il commit non ritira la chiave dalla circolazione.
</div>

---

## Domande frequenti

### Come mi autentico a un'API REST con un header X-API-Key?

Invia la tua chiave privata nell'header `X-API-Key` a ogni richiesta, ad esempio `X-API-Key: sk_your_private_key`. Le chiavi private garantiscono accesso completo a tutti gli endpoint (comprese le operazioni sincrone e asincrone, l'elaborazione batch e tutti i tipi di conversione) senza bisogno di scambio di token.

### Qual è la differenza tra le chiavi API sk_ e pk_?

Le chiavi con prefisso `sk_` sono chiavi private per l'uso server-to-server e forniscono accesso completo all'API tramite l'header `X-API-Key`. Le chiavi con prefisso `pk_` sono chiavi pubbliche per app lato client (browser): non possono chiamare l'API direttamente e devono prima essere scambiate con un JWT a breve durata tramite `POST /v1/auth/token`.

### Posso usare la mia chiave API privata (sk_) in un browser o in un'app mobile?

No. L'API rileva l'header `Origin` inviato dai browser e rifiuta le richieste effettuate con chiavi private da ambienti browser con `403 Private API keys cannot be used from browsers`. Usa invece una chiave pubblica (`pk_`) con il flusso JWT per le integrazioni lato client.

### Come ottengo un token bearer JWT per l'autenticazione lato client dell'API?

Scambia la tua chiave pubblica (`pk_`) con un JWT chiamando `POST /v1/auth/token` con la chiave nell'header `X-API-Key` e `{}` come corpo JSON. Usa il token restituito nell'header `Authorization: Bearer <token>` sulle richieste API, e aggiornalo prima della scadenza tramite `POST /v1/auth/refresh`.

### Posso limitare una chiave API privata a endpoint specifici?

Sì. Imposta `allowed_endpoints` quando crei la chiave, elencando percorsi come `/v1/convert/url-to-pdf`. Le richieste verso qualsiasi endpoint non presente nell'elenco vengono rifiutate con un errore `403 Forbidden`, a parte i percorsi di autenticazione, stato, download e per singolo job che restano raggiungibili per ogni chiave.

### Cosa succede se perdo la mia chiave API privata?

Le chiavi private sono archiviate sul server come hash SHA-256, e la chiave in chiaro viene mostrata una sola volta al momento della creazione. Se la perdi, devi generare una nuova chiave. Puoi creare più chiavi e revocare quelle vecchie dalla dashboard senza downtime.

### Quanto durano i token di accesso e i refresh token?

I token di accesso scadono dopo 1 ora e vanno conservati solo in memoria. I refresh token durano 7 giorni e sono impostati come cookie `HttpOnly` gestito dal browser.

### Perché il refresh del mio token fallisce senza credentials: "include"?

Il refresh token è archiviato in un cookie `HttpOnly` impostato durante lo scambio iniziale del token, e `POST /v1/auth/refresh` si basa sul fatto che il browser invii automaticamente quel cookie. Se ometti `credentials: "include"` dalle tue richieste fetch, il cookie non viene memorizzato né inviato. Quando un refresh fallisce, ricorri a una riautenticazione completa con la tua chiave pubblica.

### Posso usare sottodomini con wildcard nella whitelist dei domini?

Sì. `https://*.example.com` corrisponde a `https://app.example.com`, `https://staging.example.com` e all'apex `https://example.com`. Sono supportati anche host esatti e origini con porta specifica come `http://localhost:3000`. La corrispondenza ignora lo schema ma non la porta.

### Posso usare una chiave pubblica per conversioni asincrone o batch?

No. L'autenticazione con chiave pubblica supporta solo endpoint di conversione sincroni, con un singolo URL o file per richiesta; la modalità asincrona, i webhook e il polling dello stato dei batch richiedono una chiave privata. Le risposte forniscono un `presigned_url` per il download immediato.

### Come verifico se la mia chiave API è valida?

Invia una richiesta a `GET /v1/auth/verify` con una chiave privata nell'header `X-API-Key`, oppure un JWT nell'header `Authorization`. Una credenziale valida restituisce `authenticated: true` insieme al tuo `project_id` e `tier`; una non valida o scaduta restituisce `401 Unauthorized`. Una chiave pubblica non può essere verificata in questo modo e restituisce `403`.

### Perché allowed_domains e allowed_endpoints sono null nella risposta di verifica?

Entrambi i campi sono valorizzati solo per le chiavi pubbliche e restituiscono `null` per le chiavi private e per le sessioni della dashboard. Per le chiavi pubbliche, `allowed_domains` elenca i domini in whitelist e `allowed_endpoints` elenca eventuali restrizioni sugli endpoint.

### Quale metodo di autenticazione dovrei scegliere per la mia integrazione?

Usa una chiave privata (`sk_`) per servizi backend, script o strumenti interni, perché è più semplice e offre accesso completo. Usa una chiave pubblica (`pk_`) con JWT per app o widget basati su browser, poiché mantiene al sicuro le credenziali e limita l'accesso a domini in whitelist.
