# PDF Convert



<ToolHero tool="pdf-convert" />

## Visão geral

O PDF Convert é **assíncrono**:

1. `POST /convert` ou `POST /batch` → **202 Accepted** com o `conversion_id` (o `{id}` das demais rotas).
2. Um worker renderiza em background (Chromium headless).
3. Acompanhe pelo [callback `webhook_url`](/webhooks#pdf-callback) ou consultando `GET /{id}`.
4. Baixe com `GET /{id}/download` quando `status` for `completed`.

<Callout type="warn" title="Cobrança ao enfileirar">
  Os créditos são debitados quando o job é aceito. Se o enfileiramento de um batch falhar depois do débito, o valor é estornado automaticamente.
</Callout>

<EndpointList tool="pdf-convert" />

## Custos

<PricingTable tool="pdf-convert" group="document" />

<PricingTable tool="pdf-convert" group="addons" />

<PricingTable tool="pdf-convert" group="batch-discounts" />

O batch aceita até **<Limit name="pdfBatchMax" /> itens**. O retry cobra o custo integral novamente, inclusive os adicionais acima. Salvar templates e enviar fontes não custa créditos. Consultas e downloads via API seguem a [franquia diária](/cobranca-e-limites#franquia-diaria).

## Conversão

### Converter HTML em PDF/PNG/JPG

<Endpoint
  id="pdf.convert"
  example={{
  headers: { 'X-Idempotency-Key': '550e8400-e29b-41d4-a716-446655440001' },
  body: `{
"html": "<html><body><h1>Fatura</h1></body></html>",
"css": "body { font-family: sans-serif; }",
"format": "pdf",
"page_size": "A4",
"retention_days": 7
}`,
}}
>
  Envie **`html` ou `url`** (um dos dois, nunca os dois). Com `url`, o Chromium abre a página pública e a renderiza; `css`, `google_fonts_url` e `data` são recusados nesse modo (`pdf.invalid_option`).

  | Campo                                                         | Limite / valores                                                                                                          |
  | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
  | `html`                                                        | máx. <Limit name="pdfHtmlMax" />                                                                                          |
  | `url`                                                         | alternativa a `html`: endereço público `http`/`https`, até 2048 caracteres (`pdf.invalid_url` se for privado ou inválido) |
  | `css`                                                         | máx. <Limit name="pdfCssMax" />                                                                                           |
  | `format`                                                      | `pdf` (padrão), `png`, `jpg` / `jpeg`                                                                                     |
  | `file_name`                                                   | nome do arquivo gerado                                                                                                    |
  | `title`, `author`, `subject`, `keywords`                      | metadados do documento, até 1000 caracteres cada                                                                          |
  | `header_html` / `footer_html`                                 | cabeçalho e rodapé repetidos em cada página                                                                               |
  | `google_fonts_url`                                            | URL de CSS do Google Fonts a carregar                                                                                     |
  | `wait_selector` / `wait_delay`                                | espera um seletor aparecer / um atraso em ms antes de renderizar                                                          |
  | `enable_js` / `custom_js`                                     | executa JavaScript em sandbox (SPAs, gráficos) / script extra                                                             |
  | `data`                                                        | objeto com variáveis para placeholders `{{campo}}` no HTML                                                                |
  | `retention_days`                                              | `0` (efêmero, \~24h), `1`–`30` ou `-1` (permanente)                                                                       |
  | `webhook_url`                                                 | recebe o [callback](/webhooks#pdf-callback) ao terminar                                                                   |
  | `template_id`, `template_version`, `font_ids`, `post_process` | [templates salvos](#templates), [fontes](#fontes) e [pós-processamento](#pos-processamento)                               |

  Tamanho de página, margens, mídia, numeração e qualidade estão em [Opções de página e render](#opcoes-de-pagina).

  Guarde o `conversion_id`: é o `{id}` usado para consultar status e baixar o arquivo.
</Endpoint>

### Batch com template

<Endpoint
  id="pdf.batch"
  example={{
  headers: { 'X-Idempotency-Key': '550e8400-e29b-41d4-a716-446655440002' },
  body: `{
"template": {
  "html": "<html><body><h1>{{title}}</h1></body></html>",
  "css": "h1 { color: #333; }"
},
"config": { "page_size": "A4", "format": "pdf", "retention_days": 7 },
"items": [
  { "custom_id": "fatura-001", "file_name": "fatura-001.pdf", "data": { "title": "Fatura 001" } },
  { "custom_id": "fatura-002", "file_name": "fatura-002.pdf", "data": { "title": "Fatura 002" } }
],
"delivery": { "method": "zip", "webhook_url": "https://example.com/hooks/pdf" }
}`,
}}
>
  | Campo                  | Descrição                                                                                                                                                                                                                                                        |
  | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | `template.html`        | **obrigatório** — HTML com placeholders `{{campo}}` (sintaxe de Go template: `{{if}}`, `{{range}}` também funcionam)                                                                                                                                             |
  | `template.css`         | CSS compartilhado                                                                                                                                                                                                                                                |
  | `items[]`              | **obrigatório**, 1 a <Limit name="pdfBatchMax" /> itens: `custom_id` (**obrigatório**, seu identificador), `file_name` e `data`                                                                                                                                  |
  | `config`               | opções aplicadas a todos os itens: `page_size`, `orientation`, margens, `format`, `scale`, `retention_days`, `google_fonts_url`, `header_html`, `footer_html`, `wait_selector`, `wait_delay`, `title`, `author`, `enable_js`, `custom_js` e `file_name_template` |
  | `delivery.method`      | `zip` ou `list`: como o resultado é entregue ao terminar                                                                                                                                                                                                         |
  | `delivery.webhook_url` | recebe o [callback](/webhooks#pdf-callback) ao terminar                                                                                                                                                                                                          |

  O batch não aceita `url`, `template_id`, `font_ids` nem `post_process`: ele usa sempre o `template.html` do corpo. Os descontos de volume se aplicam sobre base, JavaScript e retenção. Acompanhe o progresso em [Detalhe do batch](#pdf-batch-get).
</Endpoint>

### Reprocessar falha

<Endpoint
  id="pdf.retry"
  example={{
  body: `{
"html": "<html><body><h1>Fatura corrigida</h1></body></html>",
"format": "pdf",
"page_size": "A4"
}`,
}}
>
  Permitido apenas quando `status` é `failed`. O corpo aceita as mesmas opções de [Converter](#pdf-convert), então dá para corrigir o HTML ou as opções antes de tentar de novo. O `webhook_url` do corpo é ignorado: a conversão mantém o que foi enviado originalmente. O custo é cobrado por inteiro outra vez.
</Endpoint>

## Opções de página e render

Valem para `POST /convert` e `POST /retry`. Valores fora da lista devolvem `pdf.invalid_option` (ou `pdf.invalid_page_size` para o tamanho do papel).

| Campo                                                        | Valores                                                                                                                                               |
| ------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `page_size`                                                  | `A3`, `A4` (padrão), `A5`, `Letter`, `Legal`, `Tabloid` ou `Custom`                                                                                   |
| `paper_width`, `paper_height`                                | papel personalizado, em **polegadas** (1 a 200). Só combinam com `page_size` vazio ou `Custom`; usados com outro tamanho, dão `pdf.invalid_page_size` |
| `orientation`                                                | `portrait` (padrão) ou `landscape`                                                                                                                    |
| `margin_top`, `margin_bottom`, `margin_left`, `margin_right` | margens da página                                                                                                                                     |
| `scale`                                                      | escala do render                                                                                                                                      |
| `print_background`                                           | imprime cores e imagens de fundo (padrão `true`)                                                                                                      |
| `media_type`                                                 | `print` ou `screen`: qual `@media` o CSS enxerga                                                                                                      |
| `color_scheme`                                               | `light` ou `dark`: valor de `prefers-color-scheme`                                                                                                    |
| `page_numbers`                                               | liga o contador de páginas na posição: `header-left`, `header-center`, `header-right`, `footer-left`, `footer-center` ou `footer-right`               |
| `page_number_format`                                         | texto do contador, com `{page}` e `{total}` (padrão `{page} / {total}`, até 200 caracteres). Exige `page_numbers`                                     |
| `jpeg_quality`                                               | de 1 a 100 (padrão 90). Só vale com `format: jpg`                                                                                                     |

Exemplo: fatura em papel de 8 x 11 polegadas, com contador no rodapé:

```bash
curl -sS -X POST 'https://api.respawntech.dev/v1/tools/document/pdf/convert' \
  -H "X-API-Token: $RESPAWN_API_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
  "url": "https://example.com/relatorio",
  "page_size": "Custom",
  "paper_width": 8,
  "paper_height": 11,
  "media_type": "print",
  "page_numbers": "footer-center",
  "page_number_format": "Página {page} de {total}"
}'
```

## Pós-processamento

`post_process` age sobre o PDF já renderizado e **só vale com `format: pdf`** (com `png` ou `jpg`, `pdf.invalid_option`). Cada recurso é cobrado à parte, veja [Custos](#custos).

| Campo                                   | Descrição                                                                                                                   |
| --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `post_process.watermark.text`           | texto da marca d'água, até 200 caracteres                                                                                   |
| `post_process.watermark.image`          | alternativa ao texto: imagem PNG ou JPEG em base64, até 1 MiB. Envie **exatamente um** entre `text` e `image`               |
| `watermark.font_size`                   | de 1 a 300                                                                                                                  |
| `watermark.color`                       | cor no formato `#rrggbb`                                                                                                    |
| `watermark.opacity`, `watermark.scale`  | de 0 a 1                                                                                                                    |
| `watermark.rotation`                    | de -180 a 180 graus                                                                                                         |
| `watermark.position`                    | `center`, `top-left`, `top-center`, `top-right`, `left`, `right`, `bottom-left`, `bottom-center` ou `bottom-right`          |
| `watermark.on_top`                      | `true` desenha a marca por cima do conteúdo                                                                                 |
| `post_process.pdfa`                     | `true` converte para PDF/A-2b (arquivamento de longo prazo)                                                                 |
| `post_process.protection.user_password` | **obrigatória** se houver `protection`, até 128 caracteres: senha para abrir o arquivo                                      |
| `protection.owner_password`             | senha de proprietário, até 128 caracteres                                                                                   |
| `protection.permissions`                | o que o leitor pode fazer: `print`, `copy`, `modify`, `annotate`, `fill_forms`, `assemble`. Lista vazia = nenhuma permissão |

`pdfa` e `protection` não se combinam (o PDF/A proíbe criptografia): a API responde `pdf.invalid_option`. As senhas são guardadas cifradas só para o worker aplicá-las e não voltam nas respostas.

```bash
curl -sS -X POST 'https://api.respawntech.dev/v1/tools/document/pdf/convert' \
  -H "X-API-Token: $RESPAWN_API_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
  "html": "<html><body><h1>Contrato</h1></body></html>",
  "format": "pdf",
  "post_process": {
    "watermark": { "text": "CONFIDENCIAL", "color": "#cc0000", "opacity": 0.2, "rotation": -45, "position": "center" },
    "protection": { "user_password": "senha-do-leitor", "permissions": ["print"] }
  }
}'
```

## Templates salvos

Guarde o HTML de um documento recorrente na conta e renderize-o só com `template_id` e `data`. Cada alteração de conteúdo cria uma **versão** nova; as anteriores continuam consultáveis.

* Em `POST /convert`, `template_id` não combina com `html` nem `url` (`pdf.invalid_option`). `template_version` escolhe a versão; `0` ou omitido usa a mais recente.
* O `css`, o `header_html` e o `footer_html` do template valem como padrão; se você os enviar na conversão, os seus prevalecem.
* Template ou versão inexistente: `pdf.template_not_found`. Nome de até 120 caracteres; o `html` segue os limites de <Limit name="pdfHtmlMax" /> e o `css` de <Limit name="pdfCssMax" />.
* Leituras usam o escopo `tools:pdf:read`; escritas, `tools:pdf:manage` (ou `tools:manage`). Nenhuma delas cobra créditos nem consome a franquia diária.

```bash
curl -sS -X POST 'https://api.respawntech.dev/v1/tools/document/pdf/convert' \
  -H "X-API-Token: $RESPAWN_API_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
  "template_id": "5d4c3b2a-1f0e-4d9c-8b7a-6f5e4d3c2b1a",
  "template_version": 0,
  "data": { "numero": "001" }
}'
```

### Listar templates salvos

<Endpoint id="pdf.templates.list" example={{ query: { limit: '20', offset: '0' } }}>
  `limit` (padrão e máximo 100) e `offset`. A resposta traz `items` e `total`.
</Endpoint>

### Salvar template

<Endpoint
  id="pdf.templates.create"
  example={{
  body: `{
"name": "Fatura padrão",
"description": "Fatura mensal com logotipo",
"html": "<html><body><h1>Fatura {{numero}}</h1></body></html>",
"css": "h1 { color: #333; }",
"header_html": "",
"footer_html": ""
}`,
}}
>
  `name` (até 120 caracteres) e `html` são obrigatórios; `description`, `css`, `header_html` e `footer_html` são opcionais. Cria a versão 1.
</Endpoint>

### Consultar template

<Endpoint id="pdf.templates.get" example={{ query: { version: '2' } }}>
  `version` opcional (padrão: a versão atual). Devolve o `template` e o conteúdo da `version`.
</Endpoint>

### Listar versões do template

<Endpoint id="pdf.templates.versions" />

### Renomear template ou salvar nova versão

<Endpoint
  id="pdf.templates.update"
  example={{
  body: `{
"name": "Fatura padrão v2",
"html": "<html><body><h1>Fatura {{numero}}</h1><p>{{cliente}}</p></body></html>",
"css": "h1 { color: #333; }"
}`,
}}
>
  Envie `name` e/ou `description` para renomear. Com `html`, é salva uma **nova versão** com o `html`, `css`, `header_html` e `footer_html` do corpo, e `current_version` sobe. Os campos de conteúdo omitidos ficam vazios na nova versão: reenvie o conteúdo completo.
</Endpoint>

### Excluir template

<Endpoint id="pdf.templates.delete">
  Apaga o template e todas as versões. As conversões já geradas não são afetadas.
</Endpoint>

## Fontes

Envie fontes próprias (`woff2` ou `ttf`) e use-as no HTML com `font-family`: passe os ids em `font_ids` (até 20 por conversão) e a plataforma injeta o `@font-face` correspondente. Fonte inexistente em `font_ids`: `pdf.font_not_found`. O adicional de fontes é cobrado uma vez por documento que usa `font_ids`.

* Até **50 fontes por conta** (`pdf.font_limit`).
* Uma combinação de `family` + `weight` + `style` só pode existir uma vez (`pdf.font_exists`, 409).
* O formato é detectado pelo conteúdo do arquivo, não pela extensão.

### Listar fontes

<Endpoint id="pdf.fonts.list" />

### Enviar fonte

<Endpoint id="pdf.fonts.upload">
  Envio `multipart/form-data` com `file` (obrigatório, `woff2` ou `ttf`, até 5 MiB), `family` (obrigatório, 1 a 64 letras, números, espaços, `-` ou `_`), `weight` (`100` a `900` em passos de 100; padrão `400`) e `style` (`normal` ou `italic`; padrão `normal`). Qualquer violação devolve `pdf.font_invalid`.

  ```bash
  curl -sS -X POST 'https://api.respawntech.dev/v1/tools/document/pdf/fonts' \
    -H "X-API-Token: $RESPAWN_API_TOKEN" \
    -F 'file=@Inter-Regular.woff2' \
    -F 'family=Inter' \
    -F 'weight=400' \
    -F 'style=normal'
  ```

  Depois, converta com a fonte:

  ```bash
  curl -sS -X POST 'https://api.respawntech.dev/v1/tools/document/pdf/convert' \
    -H "X-API-Token: $RESPAWN_API_TOKEN" \
    -H 'Content-Type: application/json' \
    -d '{
    "html": "<html><body style=\"font-family: Inter\"><h1>Olá</h1></body></html>",
    "font_ids": ["2b3c4d5e-6f70-4a8b-9c0d-1e2f3a4b5c6d"]
  }'
  ```
</Endpoint>

### Excluir fonte

<Endpoint id="pdf.fonts.delete">
  Remove a fonte e o arquivo armazenado.
</Endpoint>

## Consulta e download

### Listar conversões

<Endpoint id="pdf.list" example={{ query: { limit: '20', offset: '0' } }}>
  Query: `limit` (padrão 20), `offset` e `exclude_batch=true` para ocultar itens de batch. Na listagem, `file_url` e `error_message` não são preenchidos — consulte o [status](#pdf-get) de cada conversão.
</Endpoint>

### Status da conversão

<Endpoint id="pdf.get">
  `status`: `pending`, `processing`, `completed` ou `failed`. Em falha, `error_code` e `error_message` explicam o motivo. `data` traz as variáveis enviadas e `file_url` aparece quando o job termina — para baixar, use sempre [Baixar arquivo](#pdf-download).
</Endpoint>

### Baixar arquivo

<Endpoint id="pdf.download" example={{ response: 'binary' }}>
  Requer `status: completed`. O arquivo é descriptografado e descomprimido durante o download. `X-Dry-Run` não se aplica a downloads.

  O campo `file_url` da conversão (ou do detalhe do batch) é um **link assinado** com `?sig=&exp=`: ele baixa o arquivo sem o cabeçalho `X-API-Token` e sem consumir a franquia, então serve para entregar o link a um cliente final. Ele vale até a data `exp`; depois dela, consulte a conversão de novo para obter outro.
</Endpoint>

## Batches

### Listar batches

<Endpoint id="pdf.batches" example={{ query: { limit: '20', offset: '0' } }}>
  `status` do batch: `processing`, `delivering`, `completed`, `partially_failed` ou `failed`.
</Endpoint>

### Detalhe do batch

<Endpoint id="pdf.batch-get" />

### Download ZIP do batch

<Endpoint id="pdf.batch-download" example={{ response: 'binary' }}>
  Disponível quando o batch termina, inclusive parcialmente se alguns itens falharem. Também aceita o link assinado `?sig=&exp=`, sem token.
</Endpoint>

## Erros

<ErrorTable tool="pdf-convert" />

## Peculiaridades

* Um batch usa um único template HTML; cada item de `items[].data` preenche os placeholders. `custom_id` ajuda a relacionar os arquivos gerados com os seus registros.
* Para PDFs recorrentes, salve um [template](#templates) em vez de reenviar o HTML a cada chamada.
* `retention_days: 0` é efêmero (\~24h) — ideal para previews.
* Prefira `webhook_url` a consultar o status em loop: cada consulta via API conta na franquia diária.
