# PDFBolt - Scalable and Privacy‑First PDF Generation API > PDFBolt API - generate professional PDFs from HTML, URLs, and dynamic templates using REST endpoints. Supports Node.js, Python, Java, PHP, C#, Go, Rust with Direct/Sync/Async modes, AI-powered template generation, Handlebars templates, direct S3 uploads, automation platform integrations (n8n, Make, Zapier), interactive Playground, team collaboration, GDPR compliance, and enterprise-grade privacy. This file contains all documentation content in a single document following the llmstxt.org standard. ## PDFBolt API Documentation PDFBolt provides REST APIs to convert HTML, URLs, and templates into pixel-perfect PDFs and manage reusable templates. It uses headless Chromium for rendering and is built for developers and businesses that need production-grade PDF generation with privacy controls, async workflows, and S3 delivery. ## Key Features - **Three conversion modes**: Direct (PDF in response), Sync (downloadable URL), and Async (signed webhook callback). - **Dynamic templates**: Create reusable Handlebars templates in the Dashboard, generate drafts with AI, start from the gallery, or create and manage templates programmatically through the Template API. - **Privacy & data control**: HTML and template data used for conversions are redacted from stored logs after processing, PDFs auto-delete after 24 hours, and processing runs in the EU. - **Direct upload to your S3-compatible bucket**: AWS, Backblaze B2, MinIO, Wasabi, DigitalOcean Spaces. - **SDKs and examples**: Official Node.js, Python, and PHP SDKs, REST examples for Java, C#, Go, Rust, cURL, plus a Postman collection. - **Print production**: PDF/X-4, PDF/X-1a, CMYK conversion, and ICC color profiles. - **Free plan**: 100 documents per month, no credit card required. ## Get Started --- ## Quick Start Guide Generate your first PDF in a few minutes with the official [Node.js SDK](/docs/sdks/nodejs), [Python SDK](/docs/sdks/python), [PHP SDK](/docs/sdks/php), cURL, Postman, or REST examples for Java, C#, Go, and Rust. ## 1. Sign Up and Get Your API Key [Sign up](https://app.pdfbolt.com/register) for an account. Once registered, find your API key on the **API Credentials** page in your Dashboard. The free plan includes **100 document conversions per month** – no credit card required. :::tip Quick API Testing with Postman - Import the PDFBolt Postman collection to run API requests without writing code. - See the [Postman Quick Start](/docs/quick-start-guide/postman) for setup details. [](https://app.getpostman.com/run-collection/40399365-9472b2d4-c8da-4338-8774-962cc6bb9347?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D40399365-9472b2d4-c8da-4338-8774-962cc6bb9347%26entityType%3Dcollection%26workspaceId%3D3a6b1d25-d352-4c2e-8a9b-0b4fcb6d6cae#?env%5BPDFBolt%5D=W3sia2V5IjoiYmFzZV91cmwiLCJ2YWx1ZSI6Imh0dHBzOi8vYXBpLnBkZmJvbHQuY29tIiwiZW5hYmxlZCI6dHJ1ZSwidHlwZSI6ImRlZmF1bHQiLCJzZXNzaW9uVmFsdWUiOiJodHRwczovL2FwaS5wZGZib2x0LmNvbSIsImNvbXBsZXRlU2Vzc2lvblZhbHVlIjoiaHR0cHM6Ly9hcGkucGRmYm9sdC5jb20iLCJzZXNzaW9uSW5kZXgiOjB9LHsia2V5IjoiQVBJX0tFWSIsInZhbHVlIjoiIiwiZW5hYmxlZCI6dHJ1ZSwidHlwZSI6InNlY3JldCIsInNlc3Npb25WYWx1ZSI6IiIsImNvbXBsZXRlU2Vzc2lvblZhbHVlIjoiIiwic2Vzc2lvbkluZGV4IjoxfSx7ImtleSI6IndlYmhvb2tfdXJsIiwidmFsdWUiOiIiLCJlbmFibGVkIjp0cnVlLCJ0eXBlIjoiZGVmYXVsdCIsInNlc3Npb25WYWx1ZSI6IiIsImNvbXBsZXRlU2Vzc2lvblZhbHVlIjoiIiwic2Vzc2lvbkluZGV4IjoyfSx7ImtleSI6ImN1c3RvbVMzX3VybCIsInZhbHVlIjoiIiwiZW5hYmxlZCI6dHJ1ZSwidHlwZSI6ImRlZmF1bHQiLCJzZXNzaW9uVmFsdWUiOiIiLCJjb21wbGV0ZVNlc3Npb25WYWx1ZSI6IiIsInNlc3Npb25JbmRleCI6M31d) ::: ## 2. Set Up Authorization Authenticate by adding your `API-KEY` to request headers: ```bash API-KEY: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX ``` :::info Firewall Configuration If your source URLs or webhook endpoints restrict access by source IP, allowlist PDFBolt's static outbound IP addresses. See [IP Addresses](/docs/ip-addresses) for the full list. ::: ## 3. Make Your First Request Choose your endpoint based on response type: - [Direct](/docs/api-endpoints/direct) – get the PDF immediately in the response (simplest, recommended for getting started). - [Sync](/docs/api-endpoints/sync) – get a downloadable URL in a JSON response. - [Async](/docs/api-endpoints/async) – receive a webhook callback when ready (best for high-volume). **Choose your endpoint:** **Choose your source:** **Convert a webpage to PDF:** ```bash curl 'https://api.pdfbolt.com/v1/direct' \ -H 'API-KEY: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' \ -H 'Content-Type: application/json' \ -d '{ "url": "https://example.com", "format": "A4", "printBackground": true }' \ -o webpage.pdf ``` **Convert HTML to PDF:** ```bash curl 'https://api.pdfbolt.com/v1/direct' \ -H 'API-KEY: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' \ -H 'Content-Type: application/json' \ -d '{ "html": "PGh0bWw+PGJvZHk+PGgxPkhlbGxvITwvaDE+PHA+VGhpcyBpcyBhIHNhbXBsZSBQREYuPC9wPjwvYm9keT48L2h0bWw+", "format": "A4", "printBackground": true, "margin": { "top": "30px", "left": "30px" } }' \ -o document.pdf ``` :::info Base64 Explanation The base64 encoded HTML above represents: ```html Hello!This is a sample PDF. ``` ::: **Convert a template with data to PDF:** ```bash curl 'https://api.pdfbolt.com/v1/direct' \ -H 'API-KEY: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' \ -H 'Content-Type: application/json' \ -d '{ "templateId": "your-template-id", "templateData": { "client_name": "John Doe", "invoice_number": "INV-001", "total_amount": "$299.99", "line_items": [ { "description": "Web Development", "unit_price": "$200.00" }, { "description": "Design Services", "unit_price": "$99.99" } ] } }' \ -o invoice.pdf ``` :::info New to templates? - Create and publish your first template in the [Dashboard Template Designer](/docs/dashboard/templates) or through the [Template API](/docs/api-endpoints/template-api), then use its ID in conversion requests. - You can also [generate templates with AI](/docs/ai-pdf-template-generation) from descriptions or reference files. ::: :::note Expected Result The response is the raw PDF binary, saved to the file specified by your `-o` flag. Open it to view your PDF. ::: **Choose your source:** **Convert a webpage and get a download URL:** ```bash curl 'https://api.pdfbolt.com/v1/sync' \ -H 'API-KEY: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' \ -H 'Content-Type: application/json' \ -d '{ "url": "https://example.com", "format": "A4", "printBackground": true }' ``` **Convert HTML and get a download URL:** ```bash curl 'https://api.pdfbolt.com/v1/sync' \ -H 'API-KEY: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' \ -H 'Content-Type: application/json' \ -d '{ "html": "PGh0bWw+PGJvZHk+PGgxPkhlbGxvITwvaDE+PHA+VGhpcyBpcyBhIHNhbXBsZSBQREYuPC9wPjwvYm9keT48L2h0bWw+", "format": "A4", "printBackground": true, "margin": { "top": "30px", "left": "30px" } }' ``` **Convert a template with data and get a download URL:** ```bash curl 'https://api.pdfbolt.com/v1/sync' \ -H 'API-KEY: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' \ -H 'Content-Type: application/json' \ -d '{ "templateId": "your-template-id", "templateData": { "client_name": "John Doe", "invoice_number": "INV-001", "total_amount": "$299.99", "line_items": [ { "description": "Web Development", "unit_price": "$200.00" }, { "description": "Design Services", "unit_price": "$99.99" } ] } }' ``` :::info New to templates? - Create and publish your first template in the [Dashboard Template Designer](/docs/dashboard/templates) or through the [Template API](/docs/api-endpoints/template-api), then use its ID in conversion requests. - You can also [generate templates with AI](/docs/ai-pdf-template-generation) from descriptions or reference files. ::: :::note Expected Result The response is JSON with a `documentUrl` field – fetch that URL to download your PDF (valid for 24 hours). ::: :::info Plan requirement The `/v1/async` endpoint is available on paid plans. Free plan users can use `/v1/direct` and `/v1/sync`. ::: **Choose your source:** **Convert a webpage and receive a webhook callback:** ```bash curl 'https://api.pdfbolt.com/v1/async' \ -H 'API-KEY: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' \ -H 'Content-Type: application/json' \ -d '{ "url": "https://example.com", "format": "A4", "printBackground": true, "webhook": "https://your-app.com/webhook" }' ``` **Convert HTML and receive a webhook callback:** ```bash curl 'https://api.pdfbolt.com/v1/async' \ -H 'API-KEY: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' \ -H 'Content-Type: application/json' \ -d '{ "html": "PGh0bWw+PGJvZHk+PGgxPkhlbGxvITwvaDE+PHA+VGhpcyBpcyBhIHNhbXBsZSBQREYuPC9wPjwvYm9keT48L2h0bWw+", "format": "A4", "printBackground": true, "margin": { "top": "30px", "left": "30px" }, "webhook": "https://your-app.com/webhook" }' ``` **Convert a template with data and receive a webhook callback:** ```bash curl 'https://api.pdfbolt.com/v1/async' \ -H 'API-KEY: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' \ -H 'Content-Type: application/json' \ -d '{ "templateId": "your-template-id", "templateData": { "client_name": "John Doe", "invoice_number": "INV-001", "total_amount": "$299.99", "line_items": [ { "description": "Web Development", "unit_price": "$200.00" }, { "description": "Design Services", "unit_price": "$99.99" } ] }, "webhook": "https://your-app.com/webhook" }' ``` :::info New to templates? - Create and publish your first template in the [Dashboard Template Designer](/docs/dashboard/templates) or through the [Template API](/docs/api-endpoints/template-api), then use its ID in conversion requests. - You can also [generate templates with AI](/docs/ai-pdf-template-generation) from descriptions or reference files. ::: :::note Expected Result The response is JSON with a `requestId`. Your webhook URL receives a POST request with the PDF once it's ready. ::: ## 4. SDKs and Integration Guides Use an official SDK for Node.js, Python, or PHP, or follow REST quick starts for each supported language and Postman: ## Next Steps - [API Endpoints](/docs/api-endpoints) – Direct, Sync, Async modes and Usage Monitoring. - [Template API](/docs/api-endpoints/template-api) – create, validate, preview, compare, save, and publish reusable templates. - [Conversion Parameters](/docs/parameters) – customize page size, headers/footers, fonts, and more. - [Error Handling](/docs/error-handling) – HTTP status codes and recommended actions. - [Template Guide](/docs/pdf-templates) – reusable PDF layouts with Handlebars syntax. --- ## Official SDKs PDFBolt SDKs wrap the Conversion API with language-specific clients, helper methods, result objects, and error classes. Use an SDK when you want a higher-level integration than calling the REST API directly. The REST API is available from any language that can make HTTPS requests. For endpoint guides, see the [API Endpoints](/docs/api-endpoints) docs. For exact schemas and examples, use the [OpenAPI Reference](/docs/api-reference). SDKs use the same Conversion API endpoints, parameters, rate limits, and error responses as the REST API. ## Available SDKs ## When to Use an SDK Use an official SDK if you want: - Language-specific request and response helpers. - Helper methods for Direct, Sync, Async, and Usage endpoints. - Automatic raw HTML to Base64 encoding in high-level helpers. - Result helpers such as saving Direct PDF responses to disk. - API, network, validation, and webhook signature error classes. The official SDKs cover PDF conversion and usage monitoring. To create and manage templates programmatically, use the [Template API](/docs/api-endpoints/template-api) over REST. Use the REST API directly if you want full control over HTTP requests or are working in a language without an official SDK. Follow the [Quick Start Guide](/docs/quick-start-guide) or generate your own client from the [OpenAPI YAML](/openapi.yaml). --- ## Node.js SDK The official Node.js SDK is TypeScript-first, uses native `fetch`, and is intended for server-side Node.js applications. It supports PDFBolt's Direct, Sync, Async, Usage, and webhook signature workflows. If you prefer calling the REST API directly with `fetch`, see the [Node.js API quick start](/docs/quick-start-guide/nodeJS). For the complete API parameter reference, see [Conversion Parameters](/docs/parameters) and the [OpenAPI Reference](/docs/api-reference). ## Installation ```bash npm install @pdfbolt/node ``` Package: [@pdfbolt/node on npm](https://www.npmjs.com/package/@pdfbolt/node) Requires Node.js 22 or newer. ## Quick Start This example converts `https://example.com` to a PDF, saves it as `example.pdf`, and prints the SDK version and output size. ```ts const pdfbolt = new PDFBolt({ apiKey: process.env.PDFBOLT_API_KEY! }); const pdf = await pdfbolt.direct.fromUrl({ url: 'https://example.com', printBackground: true }); await pdf.save('example.pdf'); console.log(`Using PDFBolt SDK ${VERSION}`); console.log(`Saved ${pdf.size} bytes`); ``` ## Convert a URL to PDF Use `fromUrl()` when you want PDFBolt to load an HTTPS page and render it as a PDF. ```ts const pdf = await pdfbolt.direct.fromUrl({ url: 'https://example.com', format: 'A4', printBackground: true }); await pdf.save('url.pdf'); ``` ## Convert HTML to PDF Use `fromHtml()` when you have raw HTML. The SDK automatically encodes it to Base64 for the API. ```ts const pdf = await pdfbolt.direct.fromHtml({ html: 'Hello from PDFBolt', format: 'A4' }); await pdf.save('hello.pdf'); ``` If you already have a Base64-encoded HTML string, use `convert()` directly. It returns the same `DirectConversionResult` as `fromHtml()`. ```ts const pdf = await pdfbolt.direct.convert({ html: 'PGgxPkhlbGxvPC9oMT4=' }); await pdf.save('hello.pdf'); ``` Header and footer templates work the same way: `fromUrl()`, `fromHtml()`, and `fromTemplate()` accept raw HTML templates and automatically encode them to Base64, while `convert()` expects Base64-encoded template values. This rule applies to all low-level `convert()` methods: `direct.convert()`, `sync.convert()`, and `asyncConversions.convert()` send HTML and header/footer template values as provided. See the [`headerTemplate`](/docs/parameters#headertemplate) and [`footerTemplate`](/docs/parameters#footertemplate) parameter docs for supported placeholders and examples. ```ts const pdf = await pdfbolt.direct.fromHtml({ html: 'Invoice', displayHeaderFooter: true, headerTemplate: 'Invoice', footerTemplate: 'Page of ', margin: { top: '20mm', bottom: '20mm' } }); await pdf.save('invoice.pdf'); ``` ## Convert a Template to PDF Create and publish a template in the [Dashboard Template Designer](/docs/dashboard/templates) or through the [Template API](/docs/api-endpoints/template-api). Then pass its template ID and JSON data to `fromTemplate()`. ```ts const pdf = await pdfbolt.direct.fromTemplate({ templateId: '00000000-0000-0000-0000-000000000000', templateData: { invoiceNumber: 'INV-1001', customerName: 'Acme Inc.', total: '$250.00' } }); await pdf.save('template.pdf'); ``` ## Direct Results Use `pdfbolt.direct` when you want the generated PDF returned in the HTTP response. Direct conversions return a `DirectConversionResult`. `DirectConversionResult.buffer` always contains PDF bytes. When you pass `isEncoded: true`, PDFBolt returns Base64 text and the SDK exposes it as `DirectConversionResult.base64`. `DirectConversionResult.buffer` still contains decoded PDF bytes, so `save()` works the same way. ```ts const pdf = await pdfbolt.direct.fromUrl({ url: 'https://example.com', filename: 'example.pdf' }); await pdf.save('example.pdf'); console.log(pdf.buffer); // Buffer with PDF bytes console.log(pdf.base64); // string only when isEncoded: true, otherwise null console.log(pdf.size); console.log(pdf.contentType); console.log(pdf.contentDisposition); console.log(pdf.filename); console.log(pdf.conversionCost); console.log(pdf.rateLimit.minute.remaining); console.log(pdf.headers.get('x-pdfbolt-conversion-cost')); ``` Direct, Sync, Async job, and Usage results expose parsed rate-limit values through `rateLimit`. Rate-limit fields can be `null` when a response does not include the matching header. Direct results also expose raw HTTP headers via `pdf.headers`. ## Get a Temporary URL Use `pdfbolt.sync` when you want PDFBolt to generate the document and return a temporary download URL (valid for 24 hours). ```ts const result = await pdfbolt.sync.fromUrl({ url: 'https://example.com' }); console.log(result.requestId); console.log(result.status); console.log(result.documentUrl); console.log(result.expiresAt); console.log(result.duration); console.log(result.documentSizeMb); console.log(result.rateLimit.minute.remaining); console.log(result.conversionCost); ``` For custom S3 uploads, pass a valid presigned URL. PDFBolt uploads the generated PDF to your S3‑compatible bucket, so `documentUrl` and `expiresAt` are `null`. ```ts const result = await pdfbolt.sync.fromHtml({ html: 'Invoice', customS3PresignedUrl: process.env.PDFBOLT_CUSTOM_S3_PRESIGNED_URL! }); console.log(result.isCustomS3Bucket); // true console.log(result.documentUrl); // null ``` Presigned URLs are usually time-limited and often single-use. Generate a new one for each conversion. See [Uploading to Your S3 Bucket](/docs/s3-bucket-upload) for setup details. ## Run an Async Conversion Use `pdfbolt.asyncConversions` when the conversion should run in the background. The request returns an accepted job with a `requestId` immediately, and PDFBolt sends the final success or failure payload to your HTTPS webhook later. ```ts const job = await pdfbolt.asyncConversions.fromUrl({ url: 'https://example.com', webhook: 'https://your-app.com/webhooks/pdfbolt', retryDelays: [5, 15, 60] }); console.log(job.requestId); console.log(job.rateLimit.minute.remaining); ``` [`retryDelays`](/docs/api-endpoints/async#retrydelays) are in minutes and retry the conversion attempt itself, not webhook delivery. For async custom S3 uploads, pass a valid `customS3PresignedUrl` in the async request. After a successful upload, the final webhook has `isCustomS3Bucket: true`, `documentUrl: null`, and `expiresAt: null`. ## Verify Webhook Signatures Use the exact raw request body received from your framework. Do not parse and re-serialize JSON before verification. Supported raw body types are `string`, `Buffer`, `Uint8Array`, `ArrayBuffer`, and `ArrayBufferView`. When using Express, configure the webhook route with a raw body parser before calling `verifyAndParse()`: ```js express.raw({ type: 'application/json' }) ``` The `secret` value is your PDFBolt webhook signature key, not your API key. You can find the webhook signature key on the [API Credentials page](/docs/dashboard/api-keys) in the Dashboard. ```ts const event = PDFBolt.webhooks.verifyAndParse({ rawBody, signature: req.headers['x-pdfbolt-signature'], secret: process.env.PDFBOLT_WEBHOOK_SECRET! }); console.log(event.requestId); console.log(event.status); console.log(event.errorCode); console.log(event.documentUrl); ``` `verifyAndParse()` verifies the HMAC signature first and parses JSON only after the signature is valid. If you only need a boolean result, use `PDFBolt.webhooks.verifySignature()`. The SDK exposes webhook helpers through both `PDFBolt.webhooks` and the top-level `webhooks` export. Use whichever import style fits your codebase. ## Error Handling The PDFBolt API returns one common error response shape. The SDK represents API error responses with one class: `PDFBoltAPIError`. Check `statusCode` for HTTP-level handling and `errorCode` for PDFBolt-specific causes. ```ts PDFBoltAPIError, PDFBoltNetworkError, PDFBoltValidationError } from '@pdfbolt/node'; try { await pdfbolt.direct.fromUrl({ url: 'https://example.com' }); } catch (error) { if (error instanceof PDFBoltValidationError) { console.log(error.message); } else if (error instanceof PDFBoltAPIError) { console.log(error.statusCode); console.log(error.timestamp); console.log(error.errorCode); console.log(error.errorMessage); console.log(error.rateLimit.minute.limit); console.log(error.rateLimit.minute.remaining); console.log(error.rawBody); if (error.statusCode === 401) { console.log('Check your API key.'); } if (error.errorCode === 'TOO_MANY_REQUESTS') { console.log(error.rateLimit.minute.remaining); } } else if (error instanceof PDFBoltNetworkError) { console.log(error.message); } else { throw error; } } ``` `PDFBoltError` is the base class for all SDK errors. `PDFBoltAPIError` is thrown when the PDFBolt API returns an HTTP error response. Exported error classes: ```ts PDFBoltError PDFBoltAPIError PDFBoltNetworkError PDFBoltWebhookSignatureError PDFBoltValidationError PDFBoltConfigurationError ``` See [Error Handling](/docs/error-handling) for the full API error reference. These SDK-specific classes are worth calling out: - `PDFBoltValidationError` is thrown before a request is sent when a high-level helper is called with missing or invalid SDK-side parameters. - `PDFBoltConfigurationError` is thrown before a request is sent, for example when the API key is missing. - `PDFBoltNetworkError` means the SDK did not receive a usable HTTP response, for example because of a network failure, timeout, or aborted request. - `PDFBoltWebhookSignatureError` is thrown by `verifyAndParse()` when the webhook signature is invalid. ## Advanced Client Options ```ts const pdfbolt = new PDFBolt({ apiKey: process.env.PDFBOLT_API_KEY!, requestTimeoutMs: 120_000 }); ``` The SDK does not automatically retry failed requests. One SDK method call sends at most one HTTP request. For async conversion retries handled by PDFBolt, use the `retryDelays` conversion parameter. `requestTimeoutMs` is the SDK HTTP timeout. The default is `120_000` ms. The conversion `timeout` option is different: it is sent to the PDFBolt API and controls the browser render timeout for the PDF conversion. The SDK sends `User-Agent: pdfbolt-node/` on requests to the PDFBolt API. This helps identify SDK traffic for support and debugging. To set headers for the page being rendered by Chromium, use the conversion `extraHTTPHeaders` parameter. Common conversion options such as `format`, `margin`, `printBackground`, `contentDisposition`, `filename`, and `compression` use the same names as the REST API. See [Conversion Parameters](/docs/parameters) for the full parameter reference. ## CommonJS Use `require()` if your Node.js project uses CommonJS. ```js const { PDFBolt } = require('@pdfbolt/node'); const pdfbolt = new PDFBolt({ apiKey: process.env.PDFBOLT_API_KEY }); ``` ## Usage Use `pdfbolt.usage.get()` to read the current account plan, remaining conversion credits, and rate-limit metadata. ```ts const usage = await pdfbolt.usage.get(); console.log(usage.plan); console.log(usage.recurring); console.log(usage.oneTime); console.log(usage.rateLimit.day.remaining); ``` ## SDK Reference Main client methods: ```ts pdfbolt.direct.convert(...) pdfbolt.direct.fromUrl(...) pdfbolt.direct.fromHtml(...) pdfbolt.direct.fromTemplate(...) pdfbolt.sync.convert(...) pdfbolt.sync.fromUrl(...) pdfbolt.sync.fromHtml(...) pdfbolt.sync.fromTemplate(...) pdfbolt.asyncConversions.convert(...) pdfbolt.asyncConversions.fromUrl(...) pdfbolt.asyncConversions.fromHtml(...) pdfbolt.asyncConversions.fromTemplate(...) pdfbolt.usage.get(...) ``` Webhook helpers: ```ts PDFBolt.webhooks.verifySignature(...) PDFBolt.webhooks.verifyAndParse(...) webhooks.verifySignature(...) webhooks.verifyAndParse(...) ``` Common runtime exports: ```ts PDFBolt DirectConversionResult VERSION Webhooks webhooks PDFBoltError PDFBoltAPIError PDFBoltNetworkError PDFBoltWebhookSignatureError PDFBoltValidationError PDFBoltConfigurationError ``` TypeScript type exports include conversion request and result types, webhook event and verification option types, rate-limit metadata, cookies, margins, dimensions, and other REST API parameter types. --- ## Python SDK The official Python SDK is typed, uses `requests`, and is intended for server-side Python applications. It supports PDFBolt's Direct, Sync, Async, Usage, and webhook signature workflows. If you prefer calling the REST API directly with Python, see the [Python API quick start](/docs/quick-start-guide/python). For the complete API parameter reference, see [Conversion Parameters](/docs/parameters) and the [OpenAPI Reference](/docs/api-reference). ## Installation ```bash pip install pdfbolt ``` Package: [pdfbolt on PyPI](https://pypi.org/project/pdfbolt/) Requires Python 3.11 or newer. ## Quick Start This example converts `https://example.com` to a PDF, saves it as `example.pdf`, and prints the SDK version and output size. ```python from pdfbolt import PDFBolt, VERSION pdfbolt = PDFBolt(api_key=os.environ["PDFBOLT_API_KEY"]) pdf = pdfbolt.direct.from_url( url="https://example.com", print_background=True, ) pdf.save("example.pdf") print(f"Using PDFBolt SDK {VERSION}") print(f"Saved {pdf.size} bytes") ``` Python SDK options use `snake_case` and are mapped to PDFBolt REST API fields: - `print_background` -> `printBackground` - `custom_s3_presigned_url` -> `customS3PresignedUrl` - `extra_http_headers` -> `extraHTTPHeaders` `template_data` keys are sent unchanged, so they continue to match your template variables exactly. ## Convert a URL to PDF Use `from_url()` when you want PDFBolt to load an HTTPS page and render it as a PDF. ```python pdf = pdfbolt.direct.from_url( url="https://example.com", format="A4", print_background=True, ) pdf.save("url.pdf") ``` ## Convert HTML to PDF Use `from_html()` when you have raw HTML. The SDK automatically encodes it to Base64 for the API. ```python pdf = pdfbolt.direct.from_html( html="Hello from PDFBolt", format="A4", ) pdf.save("hello.pdf") ``` If you already have a Base64-encoded HTML string, use `convert()` directly. It returns the same `DirectConversionResult` as `from_html()`. ```python pdf = pdfbolt.direct.convert({ "html": "PGgxPkhlbGxvPC9oMT4=" }) pdf.save("hello.pdf") ``` Header and footer templates work the same way: `from_url()`, `from_html()`, and `from_template()` accept raw HTML templates and automatically encode them to Base64, while `convert()` expects Base64-encoded template values. This rule applies to all low-level `convert()` methods: `direct.convert()`, `sync.convert()`, and `async_conversions.convert()` send HTML and header/footer template values as provided. See the [`headerTemplate`](/docs/parameters#headertemplate) and [`footerTemplate`](/docs/parameters#footertemplate) parameter docs for supported placeholders and examples. ```python pdf = pdfbolt.direct.from_html( html="Invoice", display_header_footer=True, header_template='Invoice', footer_template='Page of ', margin={ "top": "20mm", "bottom": "20mm", }, ) pdf.save("invoice.pdf") ``` ## Convert a Template to PDF Create and publish a template in the [Dashboard Template Designer](/docs/dashboard/templates) or through the [Template API](/docs/api-endpoints/template-api). Then pass its template ID and JSON data to `from_template()`. ```python pdf = pdfbolt.direct.from_template( template_id="00000000-0000-0000-0000-000000000000", template_data={ "invoiceNumber": "INV-1001", "customerName": "Acme Inc.", "total": "$250.00", }, ) pdf.save("template.pdf") ``` `template_data` is sent as provided. The SDK does not rename keys inside your template data object. ## Direct Results Use `pdfbolt.direct` when you want the generated PDF returned in the HTTP response. Direct conversions return a `DirectConversionResult`. `DirectConversionResult.buffer` always contains PDF bytes. When you pass `is_encoded=True`, PDFBolt returns Base64 text and the SDK exposes it as `DirectConversionResult.base64`. `DirectConversionResult.buffer` still contains decoded PDF bytes, so `save()` works the same way. ```python pdf = pdfbolt.direct.from_url( url="https://example.com", filename="example.pdf", ) pdf.save("example.pdf") print(pdf.buffer) # bytes with PDF content print(pdf.base64) # string only when is_encoded=True, otherwise None print(pdf.size) print(pdf.content_type) print(pdf.content_disposition) print(pdf.filename) print(pdf.conversion_cost) print(pdf.rate_limit.minute.remaining) print(pdf.headers.get("x-pdfbolt-conversion-cost")) ``` Direct, Sync, Async job, and Usage results expose parsed rate-limit values through `rate_limit`. Rate-limit fields can be `None` when a response does not include the matching header. Direct results also expose raw HTTP headers through `pdf.headers`. ## Get a Temporary URL Use `pdfbolt.sync` when you want PDFBolt to generate the document and return a temporary download URL, valid for 24 hours. ```python result = pdfbolt.sync.from_url(url="https://example.com") print(result.request_id) print(result.status) print(result.document_url) print(result.expires_at) print(result.duration) print(result.document_size_mb) print(result.rate_limit.minute.remaining) print(result.conversion_cost) ``` For custom S3 uploads, pass a valid presigned URL. PDFBolt uploads the generated PDF to your S3-compatible bucket, so `document_url` and `expires_at` are `None`. Custom S3 uploads are available on paid plans. ```python result = pdfbolt.sync.from_html( html="Invoice", custom_s3_presigned_url=os.environ["PDFBOLT_CUSTOM_S3_PRESIGNED_URL"], ) print(result.is_custom_s3_bucket) # True print(result.document_url) # None ``` Presigned URLs are usually time-limited and often single-use. Generate a new one for each conversion. See [Uploading to Your S3 Bucket](/docs/s3-bucket-upload) for setup details. ## Run an Async Conversion Use `pdfbolt.async_conversions` when the conversion should run in the background. The request returns an accepted job with a `request_id` immediately, and PDFBolt sends the final success or failure payload to your HTTPS webhook later. ```python job = pdfbolt.async_conversions.from_url( url="https://example.com", webhook="https://your-app.com/webhooks/pdfbolt", retry_delays=[5, 15, 60], ) print(job.request_id) print(job.rate_limit.minute.remaining) ``` [`retryDelays`](/docs/api-endpoints/async#retrydelays) are in minutes and retry the conversion attempt itself, not webhook delivery. For async custom S3 uploads, pass a valid `custom_s3_presigned_url` in the async request. After a successful upload, the final webhook has `is_custom_s3_bucket=True`, `document_url=None`, and `expires_at=None`. ## Verify Webhook Signatures Use the exact raw request body received from your framework. Do not parse and re-serialize JSON before verification. Supported raw body types are `str`, `bytes`, `bytearray`, and `memoryview`. For Flask, use `request.get_data()` as the raw body. For FastAPI or Starlette, use `await request.body()`. The `secret` value is your PDFBolt webhook signature key, not your API key. You can find the webhook signature key on the [API Credentials page](/docs/dashboard/api-keys) in the Dashboard. ```python from pdfbolt import webhooks event = webhooks.verify_and_parse( raw_body=raw_body, signature=request.headers.get("x-pdfbolt-signature"), secret=os.environ["PDFBOLT_WEBHOOK_SECRET"], ) print(event.request_id) print(event.status) print(event.error_code) print(event.document_url) ``` `verify_and_parse()` verifies the HMAC signature first and parses JSON only after the signature is valid. If you only need a boolean result, use `webhooks.verify_signature()`. The SDK exposes webhook helpers through both `PDFBolt.webhooks` and the top-level `webhooks` export. Use whichever import style fits your codebase. ## Error Handling The PDFBolt API returns one common error response shape. The SDK represents API error responses with one class: `PDFBoltAPIError`. Check `status_code` for HTTP-level handling and `error_code` for PDFBolt-specific causes. ```python from pdfbolt import ( PDFBoltAPIError, PDFBoltError, PDFBoltNetworkError, PDFBoltValidationError, ) try: pdfbolt.direct.from_url(url="https://example.com") except PDFBoltValidationError as error: print(error) except PDFBoltAPIError as error: print(error.status_code) print(error.timestamp) print(error.error_code) print(error.error_message) print(error.rate_limit.minute.limit) print(error.rate_limit.minute.remaining) print(error.raw_body) if error.status_code == 401: print("Check your API key.") if error.error_code == "TOO_MANY_REQUESTS": print(error.rate_limit.minute.remaining) except PDFBoltNetworkError as error: print(error) except PDFBoltError: raise ``` `PDFBoltError` is the base class for all SDK errors. `PDFBoltAPIError` is thrown when the PDFBolt API returns an HTTP error response. Exported error classes: ```python PDFBoltError PDFBoltAPIError PDFBoltNetworkError PDFBoltWebhookSignatureError PDFBoltValidationError PDFBoltConfigurationError ``` See [Error Handling](/docs/error-handling) for the full API error reference. These SDK-specific classes are worth calling out: - `PDFBoltValidationError` is thrown before a request is sent when a high-level helper is called with missing or invalid SDK-side parameters. - `PDFBoltConfigurationError` is thrown before a request is sent, for example when the API key is missing. - `PDFBoltNetworkError` means the SDK did not receive a usable HTTP response, for example because of a network failure, timeout, or malformed success response. - `PDFBoltWebhookSignatureError` is thrown by `verify_and_parse()` when the webhook signature or payload is invalid. ## Advanced Client Options ```python from pdfbolt import PDFBolt session = requests.Session() pdfbolt = PDFBolt( api_key=os.environ["PDFBOLT_API_KEY"], base_url="https://api.pdfbolt.com", request_timeout=120.0, session=session, ) ``` The SDK does not automatically retry failed requests. One SDK method call sends at most one HTTP request. For async conversion retries handled by PDFBolt, use the `retry_delays` conversion parameter. `request_timeout` is the SDK HTTP timeout in seconds. The default is `120.0`. The conversion `timeout` option is different: it is sent to the PDFBolt API in milliseconds and controls the browser render timeout for the PDF conversion, for example `timeout=30000`. The SDK sends `User-Agent: pdfbolt-python/` on requests to the PDFBolt API. This helps identify SDK traffic for support and debugging. To set headers for the page being rendered by Chromium, use the conversion `extra_http_headers` parameter. Common conversion options such as `format`, `margin`, `print_background`, `content_disposition`, `filename`, and `compression` use Pythonic snake_case names and are mapped to the REST API request fields. See [Conversion Parameters](/docs/parameters) for the full parameter reference. ## Usage Use `pdfbolt.usage.get()` to read the current account plan, remaining conversion credits, and rate-limit metadata. ```python usage = pdfbolt.usage.get() print(usage.plan) print(usage.recurring) print(usage.one_time) print(usage.rate_limit.day.remaining) ``` ## SDK Reference Main client methods: ```python pdfbolt.direct.convert(...) pdfbolt.direct.from_url(...) pdfbolt.direct.from_html(...) pdfbolt.direct.from_template(...) pdfbolt.sync.convert(...) pdfbolt.sync.from_url(...) pdfbolt.sync.from_html(...) pdfbolt.sync.from_template(...) pdfbolt.async_conversions.convert(...) pdfbolt.async_conversions.from_url(...) pdfbolt.async_conversions.from_html(...) pdfbolt.async_conversions.from_template(...) pdfbolt.usage.get(...) ``` Webhook helpers: ```python PDFBolt.webhooks.verify_signature(...) PDFBolt.webhooks.verify_and_parse(...) webhooks.verify_signature(...) webhooks.verify_and_parse(...) ``` Common runtime exports: ```python PDFBolt DirectConversionResult VERSION Webhooks webhooks PDFBoltError PDFBoltAPIError PDFBoltNetworkError PDFBoltWebhookSignatureError PDFBoltValidationError PDFBoltConfigurationError ``` Typed exports are available for request dictionaries, conversion options, webhook events, result models, rate-limit metadata, cookies, margins, dimensions, and other PDFBolt API parameter types. --- ## PHP SDK The official PHP SDK uses Guzzle and is intended for server-side PHP applications. It supports PDFBolt's Direct, Sync, Async, Usage, and webhook signature workflows. If you prefer calling the REST API directly with PHP, see the [PHP API quick start](/docs/quick-start-guide/php). For the complete API parameter reference, see [Conversion Parameters](/docs/parameters) and the [OpenAPI Reference](/docs/api-reference). ## Installation ```bash composer require pdfbolt/pdfbolt ``` Package: [pdfbolt/pdfbolt on Packagist](https://packagist.org/packages/pdfbolt/pdfbolt) Requires PHP 8.2 or newer. ## Quick Start This example converts `https://example.com` to a PDF, saves it as `example.pdf`, and prints the SDK version and output size. ```php direct()->fromUrl('https://example.com', [ 'printBackground' => true, ]); $pdf->save('example.pdf'); echo 'Using PDFBolt SDK ' . PDFBolt::VERSION . PHP_EOL; echo 'Saved ' . $pdf->size() . ' bytes' . PHP_EOL; ``` PHP SDK conversion parameters use the same camelCase field names as the PDFBolt REST API: - `printBackground` - `customS3PresignedUrl` - `extraHTTPHeaders` - `additionalWebhookHeaders` `templateData` keys are sent unchanged, so they continue to match your template variables exactly. ## Convert a URL to PDF Use `fromUrl()` when you want PDFBolt to load an HTTPS page and render it as a PDF. ```php $pdf = $pdfbolt->direct()->fromUrl('https://example.com', [ 'format' => 'A4', 'printBackground' => true, ]); $pdf->save('url.pdf'); ``` ## Convert HTML to PDF Use `fromHtml()` when you have raw HTML. The SDK automatically encodes it to Base64 for the API. ```php $pdf = $pdfbolt->direct()->fromHtml('Hello from PDFBolt', [ 'format' => 'A4', ]); $pdf->save('hello.pdf'); ``` If you already have a Base64-encoded HTML string, use `convert()` directly. It returns the same `DirectConversionResult` as `fromHtml()`. ```php $pdf = $pdfbolt->direct()->convert([ 'html' => 'PGgxPkhlbGxvPC9oMT4=', ]); $pdf->save('hello.pdf'); ``` Header and footer templates work the same way: `fromUrl()`, `fromHtml()`, and `fromTemplate()` accept raw HTML templates and automatically encode them to Base64, while `convert()` expects Base64-encoded template values. This rule applies to all low-level `convert()` methods: `direct()->convert()`, `sync()->convert()`, and `asyncConversions()->convert()` send `html`, `headerTemplate`, and `footerTemplate` exactly as provided. See the [`headerTemplate`](/docs/parameters#headertemplate) and [`footerTemplate`](/docs/parameters#footertemplate) parameter docs for supported placeholders and examples. ```php $pdf = $pdfbolt->direct()->fromHtml( 'Invoice', [ 'displayHeaderFooter' => true, 'headerTemplate' => 'Invoice', 'footerTemplate' => 'Page of ', 'margin' => [ 'top' => '20mm', 'bottom' => '20mm', ], ], ); $pdf->save('invoice.pdf'); ``` ## Convert a Template to PDF Create and publish a template in the [Dashboard Template Designer](/docs/dashboard/templates) or through the [Template API](/docs/api-endpoints/template-api). Then pass its template ID and JSON data to `fromTemplate()`. ```php $pdf = $pdfbolt->direct()->fromTemplate( '00000000-0000-0000-0000-000000000000', [ 'invoiceNumber' => 'INV-1001', 'customerName' => 'Acme Inc.', 'total' => '$250.00', ], ); $pdf->save('template.pdf'); ``` Pass `templateData` as an associative array. The SDK does not rename keys inside your template data. For nested empty JSON objects inside `templateData`, use `(object) []`. Plain `[]` is encoded by PHP as a JSON array. ## Direct Results Use `direct()` when you want the generated PDF returned in the HTTP response. Direct conversions return a `DirectConversionResult`. `DirectConversionResult->buffer` always contains PDF bytes. When you pass `isEncoded => true`, PDFBolt returns Base64 text and the SDK exposes it as `DirectConversionResult->base64`. `DirectConversionResult->buffer` still contains decoded PDF bytes, so `save()` works the same way. ```php $pdf = $pdfbolt->direct()->fromUrl('https://example.com', [ 'filename' => 'example.pdf', ]); $pdf->save('example.pdf'); echo $pdf->buffer; echo $pdf->base64; // string only when isEncoded is true, otherwise null echo $pdf->size(); echo $pdf->contentType; echo $pdf->contentDisposition; echo $pdf->filename; echo $pdf->conversionCost; echo $pdf->rateLimit->minute->remaining; echo $pdf->headers['x-pdfbolt-conversion-cost'][0] ?? null; ``` Direct, Sync, Async job, and Usage results expose parsed rate-limit values through `rateLimit`. Rate-limit fields can be `null` when a response does not include the matching header. Direct results also expose raw HTTP headers through `$pdf->headers`. ## Get a Temporary URL Use `sync()` when you want PDFBolt to generate the document and return a temporary download URL, valid for 24 hours. ```php $result = $pdfbolt->sync()->fromUrl('https://example.com'); echo $result->requestId; echo $result->status; echo $result->documentUrl; echo $result->expiresAt; echo $result->duration; echo $result->documentSizeMb; echo $result->rateLimit->minute->remaining; echo $result->conversionCost; ``` For custom S3 uploads, pass a valid presigned URL. PDFBolt uploads the generated PDF to your S3-compatible bucket, so `documentUrl` and `expiresAt` are `null`. Custom S3 uploads are available on paid plans. ```php $result = $pdfbolt->sync()->fromHtml('Invoice', [ 'customS3PresignedUrl' => getenv('PDFBOLT_CUSTOM_S3_PRESIGNED_URL') ?: throw new RuntimeException('Set PDFBOLT_CUSTOM_S3_PRESIGNED_URL.'), ]); var_dump($result->isCustomS3Bucket); // true var_dump($result->documentUrl); // null ``` Presigned URLs are usually time-limited and often single-use. Generate a new one for each conversion. See [Uploading to Your S3 Bucket](/docs/s3-bucket-upload) for setup details. ## Run an Async Conversion Use `asyncConversions()` when the conversion should run in the background. The request returns an accepted job with a `requestId` immediately, and PDFBolt sends the final success or failure payload to your HTTPS webhook later. ```php $job = $pdfbolt->asyncConversions()->fromUrl( 'https://example.com', 'https://your-app.com/webhooks/pdfbolt', [ 'retryDelays' => [5, 15, 60], ], ); echo $job->requestId; echo $job->rateLimit->minute->remaining; ``` [`retryDelays`](/docs/api-endpoints/async#retrydelays) are in minutes and retry the conversion attempt itself, not webhook delivery. For async custom S3 uploads, pass a valid `customS3PresignedUrl` in the async request. After a successful upload, the final webhook has `isCustomS3Bucket=true`, `documentUrl=null`, and `expiresAt=null`. ## Verify Webhook Signatures Use the exact raw request body received from your framework. Do not parse and re-serialize JSON before verification. For plain PHP handlers, use `file_get_contents('php://input')`. For Laravel and Symfony, use `$request->getContent()` as the raw body. For PSR-7 frameworks, read `(string) $request->getBody()` before any middleware consumes or modifies the body stream. The `secret` value is your PDFBolt webhook signature key, not your API key. You can find the webhook signature key on the [API Credentials page](/docs/dashboard/api-keys) in the Dashboard. ```php use PDFBolt\PDFBolt; $event = PDFBolt::webhooks()->verifyAndParse( rawBody: $request->getContent(), signature: $request->headers->get('x-pdfbolt-signature'), secret: getenv('PDFBOLT_WEBHOOK_SECRET') ?: throw new RuntimeException('Set PDFBOLT_WEBHOOK_SECRET.'), ); echo $event->requestId; echo $event->status; echo $event->errorCode; echo $event->documentUrl; ``` `verifyAndParse()` verifies the HMAC signature first and parses JSON only after the signature is valid. If you only need a boolean result, use `verifySignature()`. ## Error Handling The PDFBolt API returns one common error response shape. The SDK represents API error responses with one class: `PDFBoltApiException`. Check `statusCode` for HTTP-level handling and `errorCode` for PDFBolt-specific causes. ```php use PDFBolt\Exceptions\PDFBoltApiException; use PDFBolt\Exceptions\PDFBoltException; use PDFBolt\Exceptions\PDFBoltNetworkException; use PDFBolt\Exceptions\PDFBoltValidationException; try { $pdfbolt->direct()->fromUrl('https://example.com'); } catch (PDFBoltValidationException $error) { echo $error->getMessage(); } catch (PDFBoltApiException $error) { echo $error->statusCode; echo $error->timestamp; echo $error->errorCode; echo $error->errorMessage; echo $error->rateLimit->minute->limit; echo $error->rateLimit->minute->remaining; echo $error->rawBody; if ($error->statusCode === 401) { echo 'Check your API key.'; } } catch (PDFBoltNetworkException $error) { echo $error->getMessage(); } catch (PDFBoltException $error) { throw $error; } ``` See [Error Handling](/docs/error-handling) for the full API error reference. `PDFBoltException` is the base class for all SDK errors. These SDK-specific classes are worth calling out: - `PDFBoltValidationException` is thrown before a request when SDK-side parameters are invalid. - `PDFBoltConfigurationException` is thrown before a request when SDK configuration, such as the API key or global request timeout, is missing or invalid. - `PDFBoltNetworkException` means the SDK did not receive a usable API response, for example because of a network failure, SDK HTTP timeout, or malformed success response. - `PDFBoltWebhookSignatureException` is thrown by `verifyAndParse()` when the webhook signature or payload is invalid. Available error classes: ```php PDFBoltException PDFBoltApiException PDFBoltNetworkException PDFBoltWebhookSignatureException PDFBoltValidationException PDFBoltConfigurationException ``` ## Advanced Client Options ```php use GuzzleHttp\Client; use PDFBolt\PDFBolt; $httpClient = new Client(); $pdfbolt = new PDFBolt( apiKey: getenv('PDFBOLT_API_KEY') ?: throw new RuntimeException('Set PDFBOLT_API_KEY.'), baseUrl: 'https://api.pdfbolt.com', requestTimeout: 120.0, httpClient: $httpClient, ); ``` Pass a custom Guzzle client when you need custom transport configuration such as a proxy, instrumentation, or a test handler. The SDK does not automatically retry failed requests. One SDK method call sends at most one HTTP request. If your application retries, use idempotent inputs and generate a fresh presigned URL for each custom S3 retry. For async conversion retries handled by PDFBolt, use the `retryDelays` conversion parameter. `requestTimeout` is the SDK HTTP timeout in seconds. The default is `120.0`. It is different from the conversion `timeout` option sent to the PDFBolt API, which is a browser render timeout in milliseconds, for example `timeout => 30000`. Each conversion request can override the SDK HTTP timeout by passing `requestTimeout` in the options array: ```php $pdf = $pdfbolt->direct()->fromUrl('https://example.com', [ 'requestTimeout' => 180.0, ]); ``` For usage requests, pass the SDK HTTP timeout directly: ```php $usage = $pdfbolt->usage()->get(180.0); ``` The SDK sends `User-Agent: pdfbolt-php/` on requests to the PDFBolt API. This helps identify SDK traffic for support and debugging. To set headers for the page being rendered by Chromium, use the conversion `extraHTTPHeaders` parameter. Common conversion options such as `format`, `margin`, `printBackground`, `contentDisposition`, `filename`, and `compression` use the same names as the REST API. See [Conversion Parameters](/docs/parameters) for the full parameter reference. ## Usage Use `usage()->get()` to read the current account plan, remaining conversion credits, and rate-limit metadata. ```php $usage = $pdfbolt->usage()->get(); echo $usage->plan; print_r($usage->recurring); print_r($usage->oneTime); echo $usage->rateLimit->day->remaining; ``` ## SDK Reference Main client methods: ```text $pdfbolt->direct()->convert(array $params); $pdfbolt->direct()->fromUrl(string $url, array $options = []); $pdfbolt->direct()->fromHtml(string $html, array $options = []); $pdfbolt->direct()->fromTemplate(string $templateId, array $templateData, array $options = []); $pdfbolt->sync()->convert(array $params); $pdfbolt->sync()->fromUrl(string $url, array $options = []); $pdfbolt->sync()->fromHtml(string $html, array $options = []); $pdfbolt->sync()->fromTemplate(string $templateId, array $templateData, array $options = []); $pdfbolt->asyncConversions()->convert(array $params); $pdfbolt->asyncConversions()->fromUrl(string $url, string $webhook, array $options = []); $pdfbolt->asyncConversions()->fromHtml(string $html, string $webhook, array $options = []); $pdfbolt->asyncConversions()->fromTemplate(string $templateId, array $templateData, string $webhook, array $options = []); $pdfbolt->usage()->get(?float $requestTimeout = null); ``` Webhook helpers: ```text PDFBolt::webhooks()->verifySignature(string $rawBody, string|array|null $signature, string $secret); PDFBolt::webhooks()->verifyAndParse(string $rawBody, string|array|null $signature, string $secret); ``` Common runtime classes: ```text PDFBolt DirectConversionResult SyncConversionResult AsyncConversionJob AsyncConversionWebhookEvent UsageSummary RateLimitInfo PDFBoltException PDFBoltApiException PDFBoltNetworkException PDFBoltWebhookSignatureException PDFBoltValidationException PDFBoltConfigurationException ``` Namespaces are omitted in the reference lists for readability; result classes live under `PDFBolt\Results`, exception classes under `PDFBolt\Exceptions`, and the client class lives under `PDFBolt`. --- ## Node.js PDF Generation: Quick Start Guide # Quick Start for Node.js Integrate PDFBolt's REST API in Node.js to generate PDFs from URLs, HTML, or templates. The examples below cover all three conversion modes (Direct, Sync, Async). :::tip Official Node.js SDK Want typed helper methods, automatic HTML encoding, and SDK error classes? See the [Node.js SDK guide](/docs/sdks/nodejs). ::: ## 1. Get Your API Key Find your API key on the **API Credentials** page in your Dashboard. If you don't have an account, [sign up](https://app.pdfbolt.com/register) – the free plan includes 100 document conversions per month. ## 2. Make Your First Request Any HTTP client works – adjust the request structure to match your library. Examples use native `fetch` (Node.js 18+). **Choose your endpoint:** The **Direct** endpoint provides immediate PDF generation and returns the raw PDF file in the response. **Choose your source:** **Convert a webpage to PDF:** ```js const fs = require('fs'); async function generatePdf() { const response = await fetch('https://api.pdfbolt.com/v1/direct', { method: 'POST', headers: { 'API-KEY': 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', 'Content-Type': 'application/json' }, body: JSON.stringify({ url: 'https://example.com', format: 'A4', printBackground: true }) }); if (!response.ok) { const errorText = await response.text(); throw new Error(`HTTP ${response.status} - ${errorText}`); } const pdfBuffer = await response.arrayBuffer(); fs.writeFileSync('webpage.pdf', Buffer.from(pdfBuffer)); console.log('PDF generated successfully'); } generatePdf().catch(console.error); ``` **Convert HTML to PDF** (HTML must be base64 encoded): ```js const fs = require('fs'); async function generatePdf() { const htmlContent = 'Hello!This is a sample PDF.'; const base64Html = Buffer.from(htmlContent).toString('base64'); const response = await fetch('https://api.pdfbolt.com/v1/direct', { method: 'POST', headers: { 'API-KEY': 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', 'Content-Type': 'application/json' }, body: JSON.stringify({ html: base64Html, format: 'A4', printBackground: true, margin: { top: '30px', left: '30px' } }) }); if (!response.ok) { const errorText = await response.text(); throw new Error(`HTTP ${response.status} - ${errorText}`); } const pdfBuffer = await response.arrayBuffer(); fs.writeFileSync('document.pdf', Buffer.from(pdfBuffer)); console.log('PDF generated successfully'); } generatePdf().catch(console.error); ``` **Convert a template with data to PDF:** ```js const fs = require('fs'); async function generatePdf() { const response = await fetch('https://api.pdfbolt.com/v1/direct', { method: 'POST', headers: { 'API-KEY': 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', 'Content-Type': 'application/json' }, body: JSON.stringify({ templateId: 'your-template-id', templateData: { client_name: 'John Doe', invoice_number: 'INV-001', total_amount: '$299.99', line_items: [ { description: 'Web Development', unit_price: '$200.00' }, { description: 'Design Services', unit_price: '$99.99' } ] } }) }); if (!response.ok) { const errorText = await response.text(); throw new Error(`HTTP ${response.status} - ${errorText}`); } const pdfBuffer = await response.arrayBuffer(); fs.writeFileSync('invoice.pdf', Buffer.from(pdfBuffer)); console.log('PDF generated successfully'); } generatePdf().catch(console.error); ``` :::info New to templates? Create and publish your first template in the [Dashboard Template Designer](/docs/dashboard/templates) or through the [Template API](/docs/api-endpoints/template-api), then use its ID in conversion requests. See the [Template Guide](/docs/pdf-templates) for Handlebars syntax and examples. ::: The **Sync** endpoint returns a JSON response with a download URL for the PDF (valid for 24 hours). **Choose your source:** **Convert a webpage and get a download URL:** ```js async function generatePdf() { const response = await fetch('https://api.pdfbolt.com/v1/sync', { method: 'POST', headers: { 'API-KEY': 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', 'Content-Type': 'application/json' }, body: JSON.stringify({ url: 'https://example.com', format: 'A4', printBackground: true }) }); if (!response.ok) { const errorText = await response.text(); throw new Error(`HTTP ${response.status} - ${errorText}`); } const result = await response.json(); console.log('PDF URL:', result.documentUrl); } generatePdf().catch(console.error); ``` **Convert HTML and get a download URL** (HTML must be base64 encoded): ```js async function generatePdf() { const htmlContent = 'Hello!This is a sample PDF.'; const base64Html = Buffer.from(htmlContent).toString('base64'); const response = await fetch('https://api.pdfbolt.com/v1/sync', { method: 'POST', headers: { 'API-KEY': 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', 'Content-Type': 'application/json' }, body: JSON.stringify({ html: base64Html, format: 'A4', printBackground: true, margin: { top: '30px', left: '30px' } }) }); if (!response.ok) { const errorText = await response.text(); throw new Error(`HTTP ${response.status} - ${errorText}`); } const result = await response.json(); console.log('PDF URL:', result.documentUrl); } generatePdf().catch(console.error); ``` **Convert a template with data and get a download URL:** ```js async function generatePdf() { const response = await fetch('https://api.pdfbolt.com/v1/sync', { method: 'POST', headers: { 'API-KEY': 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', 'Content-Type': 'application/json' }, body: JSON.stringify({ templateId: 'your-template-id', templateData: { client_name: 'John Doe', invoice_number: 'INV-001', total_amount: '$299.99', line_items: [ { description: 'Web Development', unit_price: '$200.00' }, { description: 'Design Services', unit_price: '$99.99' } ] } }) }); if (!response.ok) { const errorText = await response.text(); throw new Error(`HTTP ${response.status} - ${errorText}`); } const result = await response.json(); console.log('PDF URL:', result.documentUrl); } generatePdf().catch(console.error); ``` :::info New to templates? Create and publish your first template in the [Dashboard Template Designer](/docs/dashboard/templates) or through the [Template API](/docs/api-endpoints/template-api), then use its ID in conversion requests. See the [Template Guide](/docs/pdf-templates) for Handlebars syntax and examples. ::: The **Async** endpoint returns a `requestId` immediately and delivers the final result via webhook callback. **Choose your source:** **Convert a webpage and receive a webhook callback:** ```js async function generatePdf() { const response = await fetch('https://api.pdfbolt.com/v1/async', { method: 'POST', headers: { 'API-KEY': 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', 'Content-Type': 'application/json' }, body: JSON.stringify({ url: 'https://example.com', format: 'A4', printBackground: true, webhook: 'https://your-app.com/webhook' }) }); if (!response.ok) { const errorText = await response.text(); throw new Error(`HTTP ${response.status} - ${errorText}`); } const result = await response.json(); console.log('Request ID:', result.requestId); console.log('PDF will be sent to webhook when ready'); } generatePdf().catch(console.error); ``` **Convert HTML and receive a webhook callback** (HTML must be base64 encoded): ```js async function generatePdf() { const htmlContent = 'Hello!This is a sample PDF.'; const base64Html = Buffer.from(htmlContent).toString('base64'); const response = await fetch('https://api.pdfbolt.com/v1/async', { method: 'POST', headers: { 'API-KEY': 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', 'Content-Type': 'application/json' }, body: JSON.stringify({ html: base64Html, format: 'A4', printBackground: true, margin: { top: '30px', left: '30px' }, webhook: 'https://your-app.com/webhook' }) }); if (!response.ok) { const errorText = await response.text(); throw new Error(`HTTP ${response.status} - ${errorText}`); } const result = await response.json(); console.log('Request ID:', result.requestId); console.log('PDF will be sent to webhook when ready'); } generatePdf().catch(console.error); ``` **Convert a template with data and receive a webhook callback:** ```js async function generatePdf() { const response = await fetch('https://api.pdfbolt.com/v1/async', { method: 'POST', headers: { 'API-KEY': 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', 'Content-Type': 'application/json' }, body: JSON.stringify({ templateId: 'your-template-id', templateData: { client_name: 'John Doe', invoice_number: 'INV-001', total_amount: '$299.99', line_items: [ { description: 'Web Development', unit_price: '$200.00' }, { description: 'Design Services', unit_price: '$99.99' } ] }, webhook: 'https://your-app.com/webhook' }) }); if (!response.ok) { const errorText = await response.text(); throw new Error(`HTTP ${response.status} - ${errorText}`); } const result = await response.json(); console.log('Request ID:', result.requestId); console.log('PDF will be sent to webhook when ready'); } generatePdf().catch(console.error); ``` :::info New to templates? Create and publish your first template in the [Dashboard Template Designer](/docs/dashboard/templates) or through the [Template API](/docs/api-endpoints/template-api), then use its ID in conversion requests. See the [Template Guide](/docs/pdf-templates) for Handlebars syntax and examples. ::: ## Next Steps :::tip Related reading - [How to Convert HTML to PDF Using an API](/blog/how-to-convert-html-to-pdf-using-api) – complete Node.js tutorial with EJS templates and invoice example. - [How to Generate Invoice PDFs with an API](/blog/generate-invoice-pdf-api) – invoice automation with a Node.js example. ::: --- ## Python PDF Generation: Quick Start Guide # Quick Start for Python Integrate PDFBolt's REST API in Python to generate PDFs from URLs, HTML, or templates. The examples below cover all three conversion modes (Direct, Sync, Async). :::tip Official Python SDK Want type-hinted helper methods, automatic HTML encoding, and SDK error classes? See the [Python SDK guide](/docs/sdks/python). ::: ## 1. Get Your API Key Find your API key on the **API Credentials** page in your Dashboard. If you don't have an account, [sign up](https://app.pdfbolt.com/register) – the free plan includes 100 document conversions per month. ## 2. Make Your First Request Any HTTP client works – adjust the request structure to match your library. Examples use the `requests` library. Install it with: ```bash pip install requests ``` **Choose your endpoint:** The **Direct** endpoint provides immediate PDF generation and returns the raw PDF file in the response. **Choose your source:** **Convert a webpage to PDF:** ```python url = "https://api.pdfbolt.com/v1/direct" headers = { "API-KEY": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "Content-Type": "application/json" } data = { "url": "https://example.com", "format": "A4", "printBackground": True } try: response = requests.post(url, headers=headers, json=data) response.raise_for_status() with open('webpage.pdf', 'wb') as f: f.write(response.content) print("PDF generated successfully") except requests.exceptions.HTTPError: print(f"HTTP {response.status_code}") print(f"Error Message: {response.text}") except requests.exceptions.RequestException as e: print(f"Error: {e}") ``` **Convert HTML to PDF** (HTML must be base64 encoded): ```python html_content = "Hello!This is a sample PDF." base64_html = base64.b64encode(html_content.encode()).decode() url = "https://api.pdfbolt.com/v1/direct" headers = { "API-KEY": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "Content-Type": "application/json" } data = { "html": base64_html, "format": "A4", "printBackground": True, "margin": { "top": "30px", "left": "30px" } } try: response = requests.post(url, headers=headers, json=data) response.raise_for_status() with open('document.pdf', 'wb') as f: f.write(response.content) print("PDF generated successfully") except requests.exceptions.HTTPError: print(f"HTTP {response.status_code}") print(f"Error Message: {response.text}") except requests.exceptions.RequestException as e: print(f"Error: {e}") ``` **Convert a template with data to PDF:** ```python url = "https://api.pdfbolt.com/v1/direct" headers = { "API-KEY": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "Content-Type": "application/json" } data = { "templateId": "your-template-id", "templateData": { "client_name": "John Doe", "invoice_number": "INV-001", "total_amount": "$299.99", "line_items": [ {"description": "Web Development", "unit_price": "$200.00"}, {"description": "Design Services", "unit_price": "$99.99"} ] } } try: response = requests.post(url, headers=headers, json=data) response.raise_for_status() with open('invoice.pdf', 'wb') as f: f.write(response.content) print("PDF generated successfully") except requests.exceptions.HTTPError: print(f"HTTP {response.status_code}") print(f"Error Message: {response.text}") except requests.exceptions.RequestException as e: print(f"Error: {e}") ``` :::info New to templates? Create and publish your first template in the [Dashboard Template Designer](/docs/dashboard/templates) or through the [Template API](/docs/api-endpoints/template-api), then use its ID in conversion requests. See the [Template Guide](/docs/pdf-templates) for Handlebars syntax and examples. ::: The **Sync** endpoint returns a JSON response with a download URL for the PDF (valid for 24 hours). **Choose your source:** **Convert a webpage and get a download URL:** ```python url = "https://api.pdfbolt.com/v1/sync" headers = { "API-KEY": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "Content-Type": "application/json" } data = { "url": "https://example.com", "format": "A4", "printBackground": True } try: response = requests.post(url, headers=headers, json=data) response.raise_for_status() result = response.json() print(f"PDF URL: {result['documentUrl']}") except requests.exceptions.HTTPError: print(f"HTTP {response.status_code}") print(f"Error Message: {response.text}") except requests.exceptions.RequestException as e: print(f"Error: {e}") ``` **Convert HTML and get a download URL** (HTML must be base64 encoded): ```python html_content = "Hello!This is a sample PDF." base64_html = base64.b64encode(html_content.encode()).decode() url = "https://api.pdfbolt.com/v1/sync" headers = { "API-KEY": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "Content-Type": "application/json" } data = { "html": base64_html, "format": "A4", "printBackground": True, "margin": { "top": "30px", "left": "30px" } } try: response = requests.post(url, headers=headers, json=data) response.raise_for_status() result = response.json() print(f"PDF URL: {result['documentUrl']}") except requests.exceptions.HTTPError: print(f"HTTP {response.status_code}") print(f"Error Message: {response.text}") except requests.exceptions.RequestException as e: print(f"Error: {e}") ``` **Convert a template with data and get a download URL:** ```python url = "https://api.pdfbolt.com/v1/sync" headers = { "API-KEY": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "Content-Type": "application/json" } data = { "templateId": "your-template-id", "templateData": { "client_name": "John Doe", "invoice_number": "INV-001", "total_amount": "$299.99", "line_items": [ {"description": "Web Development", "unit_price": "$200.00"}, {"description": "Design Services", "unit_price": "$99.99"} ] } } try: response = requests.post(url, headers=headers, json=data) response.raise_for_status() result = response.json() print(f"PDF URL: {result['documentUrl']}") except requests.exceptions.HTTPError: print(f"HTTP {response.status_code}") print(f"Error Message: {response.text}") except requests.exceptions.RequestException as e: print(f"Error: {e}") ``` :::info New to templates? Create and publish your first template in the [Dashboard Template Designer](/docs/dashboard/templates) or through the [Template API](/docs/api-endpoints/template-api), then use its ID in conversion requests. See the [Template Guide](/docs/pdf-templates) for Handlebars syntax and examples. ::: The **Async** endpoint returns a `requestId` immediately and delivers the final result via webhook callback. **Choose your source:** **Convert a webpage and receive a webhook callback:** ```python url = "https://api.pdfbolt.com/v1/async" headers = { "API-KEY": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "Content-Type": "application/json" } data = { "url": "https://example.com", "format": "A4", "printBackground": True, "webhook": "https://your-app.com/webhook" } try: response = requests.post(url, headers=headers, json=data) response.raise_for_status() result = response.json() print(f"Request ID: {result['requestId']}") print("PDF will be sent to webhook when ready") except requests.exceptions.HTTPError: print(f"HTTP {response.status_code}") print(f"Error Message: {response.text}") except requests.exceptions.RequestException as e: print(f"Error: {e}") ``` **Convert HTML and receive a webhook callback** (HTML must be base64 encoded): ```python html_content = "Hello!This is a sample PDF." base64_html = base64.b64encode(html_content.encode()).decode() url = "https://api.pdfbolt.com/v1/async" headers = { "API-KEY": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "Content-Type": "application/json" } data = { "html": base64_html, "format": "A4", "printBackground": True, "margin": { "top": "30px", "left": "30px" }, "webhook": "https://your-app.com/webhook" } try: response = requests.post(url, headers=headers, json=data) response.raise_for_status() result = response.json() print(f"Request ID: {result['requestId']}") print("PDF will be sent to webhook when ready") except requests.exceptions.HTTPError: print(f"HTTP {response.status_code}") print(f"Error Message: {response.text}") except requests.exceptions.RequestException as e: print(f"Error: {e}") ``` **Convert a template with data and receive a webhook callback:** ```python url = "https://api.pdfbolt.com/v1/async" headers = { "API-KEY": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "Content-Type": "application/json" } data = { "templateId": "your-template-id", "templateData": { "client_name": "John Doe", "invoice_number": "INV-001", "total_amount": "$299.99", "line_items": [ {"description": "Web Development", "unit_price": "$200.00"}, {"description": "Design Services", "unit_price": "$99.99"} ] }, "webhook": "https://your-app.com/webhook" } try: response = requests.post(url, headers=headers, json=data) response.raise_for_status() result = response.json() print(f"Request ID: {result['requestId']}") print("PDF will be sent to webhook when ready") except requests.exceptions.HTTPError: print(f"HTTP {response.status_code}") print(f"Error Message: {response.text}") except requests.exceptions.RequestException as e: print(f"Error: {e}") ``` :::info New to templates? Create and publish your first template in the [Dashboard Template Designer](/docs/dashboard/templates) or through the [Template API](/docs/api-endpoints/template-api), then use its ID in conversion requests. See the [Template Guide](/docs/pdf-templates) for Handlebars syntax and examples. ::: ## Next Steps :::tip Related reading - [How to Generate Invoice PDFs with an API](/blog/generate-invoice-pdf-api) – invoice automation with a Python example. - [Print-Ready PDF Generation: PDF/X-1a and PDF/X-4 via API](/blog/print-ready-pdf-generation-pdfx1a-pdfx4) – generate print-ready PDFs, includes Python example. ::: --- ## Java PDF Generation: Quick Start Guide # Quick Start for Java Integrate PDFBolt's REST API in Java to generate PDFs from URLs, HTML, or templates. The examples below cover all three conversion modes (Direct, Sync, Async). ## 1. Get Your API Key Find your API key on the **API Credentials** page in your Dashboard. If you don't have an account, [sign up](https://app.pdfbolt.com/register) – the free plan includes 100 document conversions per month. ## 2. Make Your First Request Any HTTP client works – adjust the request structure to match your library. Examples require Java 17+ and use the built-in `java.net.http.HttpClient` and Jackson for JSON parsing. Add Jackson to your project: ```xml com.fasterxml.jackson.core jackson-databind 2.21.3 ``` ```groovy implementation 'com.fasterxml.jackson.core:jackson-databind:2.21.3' ``` **Choose your endpoint:** The **Direct** endpoint provides immediate PDF generation and returns the raw PDF file in the response. **Choose your source:** **Convert a webpage to PDF:** ```java public class DirectUrl { public static void main(String[] args) throws Exception { String jsonBody = """ { "url": "https://example.com", "format": "A4", "printBackground": true } """; var client = HttpClient.newHttpClient(); var request = HttpRequest.newBuilder() .uri(URI.create("https://api.pdfbolt.com/v1/direct")) .header("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(jsonBody)) .build(); var response = client.send(request, HttpResponse.BodyHandlers.ofByteArray()); if (response.statusCode() == 200) { Files.write(Paths.get("webpage.pdf"), response.body()); System.out.println("PDF generated successfully"); } else { System.err.println("HTTP " + response.statusCode()); System.err.println("Error Message: " + new String(response.body())); } } } ``` **Convert HTML to PDF** (HTML must be base64 encoded): ```java public class DirectHtml { public static void main(String[] args) throws Exception { String htmlContent = "Hello!This is a sample PDF."; String base64Html = Base64.getEncoder().encodeToString(htmlContent.getBytes(StandardCharsets.UTF_8)); String jsonBody = """ { "html": "%s", "format": "A4", "printBackground": true, "margin": { "top": "30px", "left": "30px" } } """.formatted(base64Html); var client = HttpClient.newHttpClient(); var request = HttpRequest.newBuilder() .uri(URI.create("https://api.pdfbolt.com/v1/direct")) .header("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(jsonBody)) .build(); var response = client.send(request, HttpResponse.BodyHandlers.ofByteArray()); if (response.statusCode() == 200) { Files.write(Paths.get("document.pdf"), response.body()); System.out.println("PDF generated successfully"); } else { System.err.println("HTTP " + response.statusCode()); System.err.println("Error Message: " + new String(response.body())); } } } ``` **Convert a template with data to PDF:** ```java public class DirectTemplate { public static void main(String[] args) throws Exception { String jsonBody = """ { "templateId": "your-template-id", "templateData": { "client_name": "John Doe", "invoice_number": "INV-001", "total_amount": "$299.99", "line_items": [ {"description": "Web Development", "unit_price": "$200.00"}, {"description": "Design Services", "unit_price": "$99.99"} ] } } """; var client = HttpClient.newHttpClient(); var request = HttpRequest.newBuilder() .uri(URI.create("https://api.pdfbolt.com/v1/direct")) .header("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(jsonBody)) .build(); var response = client.send(request, HttpResponse.BodyHandlers.ofByteArray()); if (response.statusCode() == 200) { Files.write(Paths.get("invoice.pdf"), response.body()); System.out.println("PDF generated successfully"); } else { System.err.println("HTTP " + response.statusCode()); System.err.println("Error Message: " + new String(response.body())); } } } ``` :::info New to templates? Create and publish your first template in the [Dashboard Template Designer](/docs/dashboard/templates) or through the [Template API](/docs/api-endpoints/template-api), then use its ID in conversion requests. See the [Template Guide](/docs/pdf-templates) for Handlebars syntax and examples. ::: The **Sync** endpoint returns a JSON response with a download URL for the PDF (valid for 24 hours). **Choose your source:** **Convert a webpage and get a download URL:** ```java public class SyncUrl { public static void main(String[] args) throws Exception { String jsonBody = """ { "url": "https://example.com", "format": "A4", "printBackground": true } """; var client = HttpClient.newHttpClient(); var request = HttpRequest.newBuilder() .uri(URI.create("https://api.pdfbolt.com/v1/sync")) .header("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(jsonBody)) .build(); var response = client.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() == 200) { JsonNode root = new ObjectMapper().readTree(response.body()); String documentUrl = root.get("documentUrl").asText(); System.out.println("PDF URL: " + documentUrl); } else { System.err.println("HTTP " + response.statusCode()); System.err.println("Error Message: " + response.body()); } } } ``` **Convert HTML and get a download URL** (HTML must be base64 encoded): ```java public class SyncHtml { public static void main(String[] args) throws Exception { String htmlContent = "Hello!This is a sample PDF."; String base64Html = Base64.getEncoder().encodeToString(htmlContent.getBytes(StandardCharsets.UTF_8)); String jsonBody = """ { "html": "%s", "format": "A4", "printBackground": true, "margin": { "top": "30px", "left": "30px" } } """.formatted(base64Html); var client = HttpClient.newHttpClient(); var request = HttpRequest.newBuilder() .uri(URI.create("https://api.pdfbolt.com/v1/sync")) .header("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(jsonBody)) .build(); var response = client.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() == 200) { JsonNode root = new ObjectMapper().readTree(response.body()); String documentUrl = root.get("documentUrl").asText(); System.out.println("PDF URL: " + documentUrl); } else { System.err.println("HTTP " + response.statusCode()); System.err.println("Error Message: " + response.body()); } } } ``` **Convert a template with data and get a download URL:** ```java public class SyncTemplate { public static void main(String[] args) throws Exception { String jsonBody = """ { "templateId": "your-template-id", "templateData": { "client_name": "John Doe", "invoice_number": "INV-001", "total_amount": "$299.99", "line_items": [ {"description": "Web Development", "unit_price": "$200.00"}, {"description": "Design Services", "unit_price": "$99.99"} ] } } """; var client = HttpClient.newHttpClient(); var request = HttpRequest.newBuilder() .uri(URI.create("https://api.pdfbolt.com/v1/sync")) .header("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(jsonBody)) .build(); var response = client.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() == 200) { JsonNode root = new ObjectMapper().readTree(response.body()); String documentUrl = root.get("documentUrl").asText(); System.out.println("PDF URL: " + documentUrl); } else { System.err.println("HTTP " + response.statusCode()); System.err.println("Error Message: " + response.body()); } } } ``` :::info New to templates? Create and publish your first template in the [Dashboard Template Designer](/docs/dashboard/templates) or through the [Template API](/docs/api-endpoints/template-api), then use its ID in conversion requests. See the [Template Guide](/docs/pdf-templates) for Handlebars syntax and examples. ::: The **Async** endpoint returns a `requestId` immediately and delivers the final result via webhook callback. **Choose your source:** **Convert a webpage and receive a webhook callback:** ```java public class AsyncUrl { public static void main(String[] args) throws Exception { String jsonBody = """ { "url": "https://example.com", "format": "A4", "printBackground": true, "webhook": "https://your-app.com/webhook" } """; var client = HttpClient.newHttpClient(); var request = HttpRequest.newBuilder() .uri(URI.create("https://api.pdfbolt.com/v1/async")) .header("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(jsonBody)) .build(); var response = client.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() == 200) { JsonNode root = new ObjectMapper().readTree(response.body()); String requestId = root.get("requestId").asText(); System.out.println("Request ID: " + requestId); System.out.println("PDF will be sent to webhook when ready"); } else { System.err.println("HTTP " + response.statusCode()); System.err.println("Error Message: " + response.body()); } } } ``` **Convert HTML and receive a webhook callback** (HTML must be base64 encoded): ```java public class AsyncHtml { public static void main(String[] args) throws Exception { String htmlContent = "Hello!This is a sample PDF."; String base64Html = Base64.getEncoder().encodeToString(htmlContent.getBytes(StandardCharsets.UTF_8)); String jsonBody = """ { "html": "%s", "format": "A4", "printBackground": true, "margin": { "top": "30px", "left": "30px" }, "webhook": "https://your-app.com/webhook" } """.formatted(base64Html); var client = HttpClient.newHttpClient(); var request = HttpRequest.newBuilder() .uri(URI.create("https://api.pdfbolt.com/v1/async")) .header("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(jsonBody)) .build(); var response = client.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() == 200) { JsonNode root = new ObjectMapper().readTree(response.body()); String requestId = root.get("requestId").asText(); System.out.println("Request ID: " + requestId); System.out.println("PDF will be sent to webhook when ready"); } else { System.err.println("HTTP " + response.statusCode()); System.err.println("Error Message: " + response.body()); } } } ``` **Convert a template with data and receive a webhook callback:** ```java public class AsyncTemplate { public static void main(String[] args) throws Exception { String jsonBody = """ { "templateId": "your-template-id", "templateData": { "client_name": "John Doe", "invoice_number": "INV-001", "total_amount": "$299.99", "line_items": [ {"description": "Web Development", "unit_price": "$200.00"}, {"description": "Design Services", "unit_price": "$99.99"} ] }, "webhook": "https://your-app.com/webhook" } """; var client = HttpClient.newHttpClient(); var request = HttpRequest.newBuilder() .uri(URI.create("https://api.pdfbolt.com/v1/async")) .header("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(jsonBody)) .build(); var response = client.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() == 200) { JsonNode root = new ObjectMapper().readTree(response.body()); String requestId = root.get("requestId").asText(); System.out.println("Request ID: " + requestId); System.out.println("PDF will be sent to webhook when ready"); } else { System.err.println("HTTP " + response.statusCode()); System.err.println("Error Message: " + response.body()); } } } ``` :::info New to templates? Create and publish your first template in the [Dashboard Template Designer](/docs/dashboard/templates) or through the [Template API](/docs/api-endpoints/template-api), then use its ID in conversion requests. See the [Template Guide](/docs/pdf-templates) for Handlebars syntax and examples. ::: ## Next Steps :::tip Related reading - [Optimizing HTML for Professional PDF Output](/blog/optimizing-html-for-pdf) – HTML/CSS techniques: page breaks, fonts, and image optimization. - [How to Compress PDFs with Apache PDFBox in Java](/blog/pdfbox-compress-pdf-java) – Java PDF compression with Apache PDFBox, plus when a PDF generation API is the simpler alternative. ::: --- ## PHP PDF Generation: Quick Start Guide # Quick Start for PHP Integrate PDFBolt's REST API in PHP to generate PDFs from URLs, HTML, or templates. The examples below cover all three conversion modes (Direct, Sync, Async). :::tip Official PHP SDK Want helper methods, automatic HTML encoding, and SDK error classes? See the [PHP SDK guide](/docs/sdks/php). ::: ## 1. Get Your API Key Find your API key on the **API Credentials** page in your Dashboard. If you don't have an account, [sign up](https://app.pdfbolt.com/register) – the free plan includes 100 document conversions per month. ## 2. Make Your First Request Any HTTP client works – adjust the request structure to match your library. Examples use Guzzle. Install it with: ```bash composer require guzzlehttp/guzzle ``` **Choose your endpoint:** The **Direct** endpoint provides immediate PDF generation and returns the raw PDF file in the response. **Choose your source:** **Convert a webpage to PDF:** ```php 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', 'Content-Type' => 'application/json', ]; $body = [ 'url' => 'https://example.com', 'format' => 'A4', 'printBackground' => true, ]; try { $client = new Client(); $response = $client->post('https://api.pdfbolt.com/v1/direct', [ 'headers' => $headers, 'json' => $body, ]); file_put_contents('webpage.pdf', (string) $response->getBody()); echo "PDF generated successfully\n"; } catch (RequestException $e) { if ($e->hasResponse()) { echo "HTTP " . $e->getResponse()->getStatusCode() . "\n"; echo "Error Message: " . $e->getResponse()->getBody() . "\n"; } else { echo "Error: " . $e->getMessage() . "\n"; } } ``` **Convert HTML to PDF** (HTML must be base64 encoded): ```php Hello!This is a sample PDF.'; $base64Html = base64_encode($htmlContent); $headers = [ 'API-KEY' => 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', 'Content-Type' => 'application/json', ]; $body = [ 'html' => $base64Html, 'format' => 'A4', 'printBackground' => true, 'margin' => [ 'top' => '30px', 'left' => '30px', ], ]; try { $client = new Client(); $response = $client->post('https://api.pdfbolt.com/v1/direct', [ 'headers' => $headers, 'json' => $body, ]); file_put_contents('document.pdf', (string) $response->getBody()); echo "PDF generated successfully\n"; } catch (RequestException $e) { if ($e->hasResponse()) { echo "HTTP " . $e->getResponse()->getStatusCode() . "\n"; echo "Error Message: " . $e->getResponse()->getBody() . "\n"; } else { echo "Error: " . $e->getMessage() . "\n"; } } ``` **Convert a template with data to PDF:** ```php 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', 'Content-Type' => 'application/json', ]; $body = [ 'templateId' => 'your-template-id', 'templateData' => [ 'client_name' => 'John Doe', 'invoice_number' => 'INV-001', 'total_amount' => '$299.99', 'line_items' => [ ['description' => 'Web Development', 'unit_price' => '$200.00'], ['description' => 'Design Services', 'unit_price' => '$99.99'], ], ], ]; try { $client = new Client(); $response = $client->post('https://api.pdfbolt.com/v1/direct', [ 'headers' => $headers, 'json' => $body, ]); file_put_contents('invoice.pdf', (string) $response->getBody()); echo "PDF generated successfully\n"; } catch (RequestException $e) { if ($e->hasResponse()) { echo "HTTP " . $e->getResponse()->getStatusCode() . "\n"; echo "Error Message: " . $e->getResponse()->getBody() . "\n"; } else { echo "Error: " . $e->getMessage() . "\n"; } } ``` :::info New to templates? Create and publish your first template in the [Dashboard Template Designer](/docs/dashboard/templates) or through the [Template API](/docs/api-endpoints/template-api), then use its ID in conversion requests. See the [Template Guide](/docs/pdf-templates) for Handlebars syntax and examples. ::: The **Sync** endpoint returns a JSON response with a download URL for the PDF (valid for 24 hours). **Choose your source:** **Convert a webpage and get a download URL:** ```php 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', 'Content-Type' => 'application/json', ]; $body = [ 'url' => 'https://example.com', 'format' => 'A4', 'printBackground' => true, ]; try { $client = new Client(); $response = $client->post('https://api.pdfbolt.com/v1/sync', [ 'headers' => $headers, 'json' => $body, ]); $result = json_decode((string) $response->getBody(), true); echo "PDF URL: " . $result['documentUrl'] . "\n"; } catch (RequestException $e) { if ($e->hasResponse()) { echo "HTTP " . $e->getResponse()->getStatusCode() . "\n"; echo "Error Message: " . $e->getResponse()->getBody() . "\n"; } else { echo "Error: " . $e->getMessage() . "\n"; } } ``` **Convert HTML and get a download URL** (HTML must be base64 encoded): ```php Hello!This is a sample PDF.'; $base64Html = base64_encode($htmlContent); $headers = [ 'API-KEY' => 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', 'Content-Type' => 'application/json', ]; $body = [ 'html' => $base64Html, 'format' => 'A4', 'printBackground' => true, 'margin' => [ 'top' => '30px', 'left' => '30px', ], ]; try { $client = new Client(); $response = $client->post('https://api.pdfbolt.com/v1/sync', [ 'headers' => $headers, 'json' => $body, ]); $result = json_decode((string) $response->getBody(), true); echo "PDF URL: " . $result['documentUrl'] . "\n"; } catch (RequestException $e) { if ($e->hasResponse()) { echo "HTTP " . $e->getResponse()->getStatusCode() . "\n"; echo "Error Message: " . $e->getResponse()->getBody() . "\n"; } else { echo "Error: " . $e->getMessage() . "\n"; } } ``` **Convert a template with data and get a download URL:** ```php 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', 'Content-Type' => 'application/json', ]; $body = [ 'templateId' => 'your-template-id', 'templateData' => [ 'client_name' => 'John Doe', 'invoice_number' => 'INV-001', 'total_amount' => '$299.99', 'line_items' => [ ['description' => 'Web Development', 'unit_price' => '$200.00'], ['description' => 'Design Services', 'unit_price' => '$99.99'], ], ], ]; try { $client = new Client(); $response = $client->post('https://api.pdfbolt.com/v1/sync', [ 'headers' => $headers, 'json' => $body, ]); $result = json_decode((string) $response->getBody(), true); echo "PDF URL: " . $result['documentUrl'] . "\n"; } catch (RequestException $e) { if ($e->hasResponse()) { echo "HTTP " . $e->getResponse()->getStatusCode() . "\n"; echo "Error Message: " . $e->getResponse()->getBody() . "\n"; } else { echo "Error: " . $e->getMessage() . "\n"; } } ``` :::info New to templates? Create and publish your first template in the [Dashboard Template Designer](/docs/dashboard/templates) or through the [Template API](/docs/api-endpoints/template-api), then use its ID in conversion requests. See the [Template Guide](/docs/pdf-templates) for Handlebars syntax and examples. ::: The **Async** endpoint returns a `requestId` immediately and delivers the final result via webhook callback. **Choose your source:** **Convert a webpage and receive a webhook callback:** ```php 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', 'Content-Type' => 'application/json', ]; $body = [ 'url' => 'https://example.com', 'format' => 'A4', 'printBackground' => true, 'webhook' => 'https://your-app.com/webhook', ]; try { $client = new Client(); $response = $client->post('https://api.pdfbolt.com/v1/async', [ 'headers' => $headers, 'json' => $body, ]); $result = json_decode((string) $response->getBody(), true); echo "Request ID: " . $result['requestId'] . "\n"; echo "PDF will be sent to webhook when ready\n"; } catch (RequestException $e) { if ($e->hasResponse()) { echo "HTTP " . $e->getResponse()->getStatusCode() . "\n"; echo "Error Message: " . $e->getResponse()->getBody() . "\n"; } else { echo "Error: " . $e->getMessage() . "\n"; } } ``` **Convert HTML and receive a webhook callback** (HTML must be base64 encoded): ```php Hello!This is a sample PDF.'; $base64Html = base64_encode($htmlContent); $headers = [ 'API-KEY' => 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', 'Content-Type' => 'application/json', ]; $body = [ 'html' => $base64Html, 'format' => 'A4', 'printBackground' => true, 'margin' => [ 'top' => '30px', 'left' => '30px', ], 'webhook' => 'https://your-app.com/webhook', ]; try { $client = new Client(); $response = $client->post('https://api.pdfbolt.com/v1/async', [ 'headers' => $headers, 'json' => $body, ]); $result = json_decode((string) $response->getBody(), true); echo "Request ID: " . $result['requestId'] . "\n"; echo "PDF will be sent to webhook when ready\n"; } catch (RequestException $e) { if ($e->hasResponse()) { echo "HTTP " . $e->getResponse()->getStatusCode() . "\n"; echo "Error Message: " . $e->getResponse()->getBody() . "\n"; } else { echo "Error: " . $e->getMessage() . "\n"; } } ``` **Convert a template with data and receive a webhook callback:** ```php 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', 'Content-Type' => 'application/json', ]; $body = [ 'templateId' => 'your-template-id', 'templateData' => [ 'client_name' => 'John Doe', 'invoice_number' => 'INV-001', 'total_amount' => '$299.99', 'line_items' => [ ['description' => 'Web Development', 'unit_price' => '$200.00'], ['description' => 'Design Services', 'unit_price' => '$99.99'], ], ], 'webhook' => 'https://your-app.com/webhook', ]; try { $client = new Client(); $response = $client->post('https://api.pdfbolt.com/v1/async', [ 'headers' => $headers, 'json' => $body, ]); $result = json_decode((string) $response->getBody(), true); echo "Request ID: " . $result['requestId'] . "\n"; echo "PDF will be sent to webhook when ready\n"; } catch (RequestException $e) { if ($e->hasResponse()) { echo "HTTP " . $e->getResponse()->getStatusCode() . "\n"; echo "Error Message: " . $e->getResponse()->getBody() . "\n"; } else { echo "Error: " . $e->getMessage() . "\n"; } } ``` :::info New to templates? Create and publish your first template in the [Dashboard Template Designer](/docs/dashboard/templates) or through the [Template API](/docs/api-endpoints/template-api), then use its ID in conversion requests. See the [Template Guide](/docs/pdf-templates) for Handlebars syntax and examples. ::: ## Next Steps :::tip Related reading - [Optimizing HTML for Professional PDF Output](/blog/optimizing-html-for-pdf) – HTML/CSS techniques: page breaks, fonts, and image optimization. - [Top HTML Template Engines for Dynamic PDF Generation](/blog/html-template-engines) – covers Twig and Smarty for PHP templating workflows. ::: --- ## C# .NET PDF Generation: Quick Start Guide # Quick Start for C# Integrate PDFBolt's REST API in C# to generate PDFs from URLs, HTML, or templates. The examples below cover all three conversion modes (Direct, Sync, Async). ## 1. Get Your API Key Find your API key on the **API Credentials** page in your Dashboard. If you don't have an account, [sign up](https://app.pdfbolt.com/register) – the free plan includes 100 document conversions per month. ## 2. Make Your First Request Any HTTP client works – adjust the request structure to match your library. Examples use the built-in `HttpClient` and `System.Text.Json`. **Choose your endpoint:** The **Direct** endpoint provides immediate PDF generation and returns the raw PDF file in the response. **Choose your source:** **Convert a webpage to PDF:** ```csharp using System; using System.IO; using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading.Tasks; public class DirectUrl { public static async Task Main(string[] args) { using var client = new HttpClient(); var requestData = new { url = "https://example.com", format = "A4", printBackground = true }; var request = new HttpRequestMessage { Method = HttpMethod.Post, RequestUri = new Uri("https://api.pdfbolt.com/v1/direct"), Content = new StringContent( JsonSerializer.Serialize(requestData), Encoding.UTF8, "application/json" ) }; request.Headers.Add("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"); try { using var response = await client.SendAsync(request); if (!response.IsSuccessStatusCode) { var errorContent = await response.Content.ReadAsStringAsync(); Console.WriteLine($"HTTP {(int)response.StatusCode}"); Console.WriteLine($"Error Message: {errorContent}"); return; } var pdfBytes = await response.Content.ReadAsByteArrayAsync(); await File.WriteAllBytesAsync("webpage.pdf", pdfBytes); Console.WriteLine("PDF generated successfully"); } catch (Exception ex) { Console.WriteLine($"Error: {ex.Message}"); } } } ``` **Convert HTML to PDF** (HTML must be base64 encoded): ```csharp using System; using System.IO; using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading.Tasks; public class DirectHtml { public static async Task Main(string[] args) { using var client = new HttpClient(); string htmlContent = "Hello!This is a sample PDF."; string base64Html = Convert.ToBase64String(Encoding.UTF8.GetBytes(htmlContent)); var requestData = new { html = base64Html, format = "A4", printBackground = true, margin = new { top = "30px", left = "30px" } }; var request = new HttpRequestMessage { Method = HttpMethod.Post, RequestUri = new Uri("https://api.pdfbolt.com/v1/direct"), Content = new StringContent( JsonSerializer.Serialize(requestData), Encoding.UTF8, "application/json" ) }; request.Headers.Add("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"); try { using var response = await client.SendAsync(request); if (!response.IsSuccessStatusCode) { var errorContent = await response.Content.ReadAsStringAsync(); Console.WriteLine($"HTTP {(int)response.StatusCode}"); Console.WriteLine($"Error Message: {errorContent}"); return; } var pdfBytes = await response.Content.ReadAsByteArrayAsync(); await File.WriteAllBytesAsync("document.pdf", pdfBytes); Console.WriteLine("PDF generated successfully"); } catch (Exception ex) { Console.WriteLine($"Error: {ex.Message}"); } } } ``` **Convert a template with data to PDF:** ```csharp using System; using System.IO; using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading.Tasks; public class DirectTemplate { public static async Task Main(string[] args) { using var client = new HttpClient(); var requestData = new { templateId = "your-template-id", templateData = new { client_name = "John Doe", invoice_number = "INV-001", total_amount = "$299.99", line_items = new object[] { new { description = "Web Development", unit_price = "$200.00" }, new { description = "Design Services", unit_price = "$99.99" } } } }; var request = new HttpRequestMessage { Method = HttpMethod.Post, RequestUri = new Uri("https://api.pdfbolt.com/v1/direct"), Content = new StringContent( JsonSerializer.Serialize(requestData), Encoding.UTF8, "application/json" ) }; request.Headers.Add("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"); try { using var response = await client.SendAsync(request); if (!response.IsSuccessStatusCode) { var errorContent = await response.Content.ReadAsStringAsync(); Console.WriteLine($"HTTP {(int)response.StatusCode}"); Console.WriteLine($"Error Message: {errorContent}"); return; } var pdfBytes = await response.Content.ReadAsByteArrayAsync(); await File.WriteAllBytesAsync("invoice.pdf", pdfBytes); Console.WriteLine("PDF generated successfully"); } catch (Exception ex) { Console.WriteLine($"Error: {ex.Message}"); } } } ``` :::info New to templates? Create and publish your first template in the [Dashboard Template Designer](/docs/dashboard/templates) or through the [Template API](/docs/api-endpoints/template-api), then use its ID in conversion requests. See the [Template Guide](/docs/pdf-templates) for Handlebars syntax and examples. ::: The **Sync** endpoint returns a JSON response with a download URL for the PDF (valid for 24 hours). **Choose your source:** **Convert a webpage and get a download URL:** ```csharp using System; using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading.Tasks; public class SyncUrl { public static async Task Main(string[] args) { using var client = new HttpClient(); var requestData = new { url = "https://example.com", format = "A4", printBackground = true }; var request = new HttpRequestMessage { Method = HttpMethod.Post, RequestUri = new Uri("https://api.pdfbolt.com/v1/sync"), Content = new StringContent( JsonSerializer.Serialize(requestData), Encoding.UTF8, "application/json" ) }; request.Headers.Add("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"); try { using var response = await client.SendAsync(request); if (!response.IsSuccessStatusCode) { var errorContent = await response.Content.ReadAsStringAsync(); Console.WriteLine($"HTTP {(int)response.StatusCode}"); Console.WriteLine($"Error Message: {errorContent}"); return; } var responseContent = await response.Content.ReadAsStringAsync(); var result = JsonSerializer.Deserialize(responseContent); Console.WriteLine($"PDF URL: {result.GetProperty("documentUrl").GetString()}"); } catch (Exception ex) { Console.WriteLine($"Error: {ex.Message}"); } } } ``` **Convert HTML and get a download URL** (HTML must be base64 encoded): ```csharp using System; using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading.Tasks; public class SyncHtml { public static async Task Main(string[] args) { using var client = new HttpClient(); string htmlContent = "Hello!This is a sample PDF."; string base64Html = Convert.ToBase64String(Encoding.UTF8.GetBytes(htmlContent)); var requestData = new { html = base64Html, format = "A4", printBackground = true, margin = new { top = "30px", left = "30px" } }; var request = new HttpRequestMessage { Method = HttpMethod.Post, RequestUri = new Uri("https://api.pdfbolt.com/v1/sync"), Content = new StringContent( JsonSerializer.Serialize(requestData), Encoding.UTF8, "application/json" ) }; request.Headers.Add("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"); try { using var response = await client.SendAsync(request); if (!response.IsSuccessStatusCode) { var errorContent = await response.Content.ReadAsStringAsync(); Console.WriteLine($"HTTP {(int)response.StatusCode}"); Console.WriteLine($"Error Message: {errorContent}"); return; } var responseContent = await response.Content.ReadAsStringAsync(); var result = JsonSerializer.Deserialize(responseContent); Console.WriteLine($"PDF URL: {result.GetProperty("documentUrl").GetString()}"); } catch (Exception ex) { Console.WriteLine($"Error: {ex.Message}"); } } } ``` **Convert a template with data and get a download URL:** ```csharp using System; using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading.Tasks; public class SyncTemplate { public static async Task Main(string[] args) { using var client = new HttpClient(); var requestData = new { templateId = "your-template-id", templateData = new { client_name = "John Doe", invoice_number = "INV-001", total_amount = "$299.99", line_items = new object[] { new { description = "Web Development", unit_price = "$200.00" }, new { description = "Design Services", unit_price = "$99.99" } } } }; var request = new HttpRequestMessage { Method = HttpMethod.Post, RequestUri = new Uri("https://api.pdfbolt.com/v1/sync"), Content = new StringContent( JsonSerializer.Serialize(requestData), Encoding.UTF8, "application/json" ) }; request.Headers.Add("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"); try { using var response = await client.SendAsync(request); if (!response.IsSuccessStatusCode) { var errorContent = await response.Content.ReadAsStringAsync(); Console.WriteLine($"HTTP {(int)response.StatusCode}"); Console.WriteLine($"Error Message: {errorContent}"); return; } var responseContent = await response.Content.ReadAsStringAsync(); var result = JsonSerializer.Deserialize(responseContent); Console.WriteLine($"PDF URL: {result.GetProperty("documentUrl").GetString()}"); } catch (Exception ex) { Console.WriteLine($"Error: {ex.Message}"); } } } ``` :::info New to templates? Create and publish your first template in the [Dashboard Template Designer](/docs/dashboard/templates) or through the [Template API](/docs/api-endpoints/template-api), then use its ID in conversion requests. See the [Template Guide](/docs/pdf-templates) for Handlebars syntax and examples. ::: The **Async** endpoint returns a `requestId` immediately and delivers the final result via webhook callback. **Choose your source:** **Convert a webpage and receive a webhook callback:** ```csharp using System; using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading.Tasks; public class AsyncUrl { public static async Task Main(string[] args) { using var client = new HttpClient(); var requestData = new { url = "https://example.com", format = "A4", printBackground = true, webhook = "https://your-app.com/webhook" }; var request = new HttpRequestMessage { Method = HttpMethod.Post, RequestUri = new Uri("https://api.pdfbolt.com/v1/async"), Content = new StringContent( JsonSerializer.Serialize(requestData), Encoding.UTF8, "application/json" ) }; request.Headers.Add("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"); try { using var response = await client.SendAsync(request); if (!response.IsSuccessStatusCode) { var errorContent = await response.Content.ReadAsStringAsync(); Console.WriteLine($"HTTP {(int)response.StatusCode}"); Console.WriteLine($"Error Message: {errorContent}"); return; } var responseContent = await response.Content.ReadAsStringAsync(); var result = JsonSerializer.Deserialize(responseContent); Console.WriteLine($"Request ID: {result.GetProperty("requestId").GetString()}"); Console.WriteLine("PDF will be sent to webhook when ready"); } catch (Exception ex) { Console.WriteLine($"Error: {ex.Message}"); } } } ``` **Convert HTML and receive a webhook callback** (HTML must be base64 encoded): ```csharp using System; using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading.Tasks; public class AsyncHtml { public static async Task Main(string[] args) { using var client = new HttpClient(); string htmlContent = "Hello!This is a sample PDF."; string base64Html = Convert.ToBase64String(Encoding.UTF8.GetBytes(htmlContent)); var requestData = new { html = base64Html, format = "A4", printBackground = true, margin = new { top = "30px", left = "30px" }, webhook = "https://your-app.com/webhook" }; var request = new HttpRequestMessage { Method = HttpMethod.Post, RequestUri = new Uri("https://api.pdfbolt.com/v1/async"), Content = new StringContent( JsonSerializer.Serialize(requestData), Encoding.UTF8, "application/json" ) }; request.Headers.Add("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"); try { using var response = await client.SendAsync(request); if (!response.IsSuccessStatusCode) { var errorContent = await response.Content.ReadAsStringAsync(); Console.WriteLine($"HTTP {(int)response.StatusCode}"); Console.WriteLine($"Error Message: {errorContent}"); return; } var responseContent = await response.Content.ReadAsStringAsync(); var result = JsonSerializer.Deserialize(responseContent); Console.WriteLine($"Request ID: {result.GetProperty("requestId").GetString()}"); Console.WriteLine("PDF will be sent to webhook when ready"); } catch (Exception ex) { Console.WriteLine($"Error: {ex.Message}"); } } } ``` **Convert a template with data and receive a webhook callback:** ```csharp using System; using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading.Tasks; public class AsyncTemplate { public static async Task Main(string[] args) { using var client = new HttpClient(); var requestData = new { templateId = "your-template-id", templateData = new { client_name = "John Doe", invoice_number = "INV-001", total_amount = "$299.99", line_items = new object[] { new { description = "Web Development", unit_price = "$200.00" }, new { description = "Design Services", unit_price = "$99.99" } } }, webhook = "https://your-app.com/webhook" }; var request = new HttpRequestMessage { Method = HttpMethod.Post, RequestUri = new Uri("https://api.pdfbolt.com/v1/async"), Content = new StringContent( JsonSerializer.Serialize(requestData), Encoding.UTF8, "application/json" ) }; request.Headers.Add("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"); try { using var response = await client.SendAsync(request); if (!response.IsSuccessStatusCode) { var errorContent = await response.Content.ReadAsStringAsync(); Console.WriteLine($"HTTP {(int)response.StatusCode}"); Console.WriteLine($"Error Message: {errorContent}"); return; } var responseContent = await response.Content.ReadAsStringAsync(); var result = JsonSerializer.Deserialize(responseContent); Console.WriteLine($"Request ID: {result.GetProperty("requestId").GetString()}"); Console.WriteLine("PDF will be sent to webhook when ready"); } catch (Exception ex) { Console.WriteLine($"Error: {ex.Message}"); } } } ``` :::info New to templates? Create and publish your first template in the [Dashboard Template Designer](/docs/dashboard/templates) or through the [Template API](/docs/api-endpoints/template-api), then use its ID in conversion requests. See the [Template Guide](/docs/pdf-templates) for Handlebars syntax and examples. ::: ## Next Steps :::tip Related reading - [Optimizing HTML for Professional PDF Output](/blog/optimizing-html-for-pdf) – HTML/CSS techniques: page breaks, fonts, and image optimization. - [Compress PDF via API: Reduce File Size Programmatically](/blog/compress-pdf-api) – reduce PDF file size via the PDFBolt API. ::: --- ## Go PDF Generation: Quick Start Guide # Quick Start for Go Integrate PDFBolt's REST API in Go to generate PDFs from URLs, HTML, or templates. The examples below cover all three conversion modes (Direct, Sync, Async). ## 1. Get Your API Key Find your API key on the **API Credentials** page in your Dashboard. If you don't have an account, [sign up](https://app.pdfbolt.com/register) – the free plan includes 100 document conversions per month. ## 2. Make Your First Request Any HTTP client works – adjust the request structure to match your library. Examples use the built-in `net/http` and `encoding/json` packages. **Choose your endpoint:** The **Direct** endpoint provides immediate PDF generation and returns the raw PDF file in the response. **Choose your source:** **Convert a webpage to PDF:** ```go package main "bytes" "encoding/json" "fmt" "io" "log" "net/http" "os" ) func main() { data := map[string]interface{}{ "url": "https://example.com", "format": "A4", "printBackground": true, } jsonBody, err := json.Marshal(data) if err != nil { log.Fatal(err) } req, err := http.NewRequest("POST", "https://api.pdfbolt.com/v1/direct", bytes.NewReader(jsonBody)) if err != nil { log.Fatal(err) } req.Header.Add("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") req.Header.Add("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { log.Fatal(err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { body, err := io.ReadAll(resp.Body) if err != nil { log.Fatal(err) } fmt.Printf("HTTP %d\n", resp.StatusCode) fmt.Printf("Error Message: %s\n", string(body)) return } file, err := os.Create("webpage.pdf") if err != nil { log.Fatal(err) } defer file.Close() if _, err := io.Copy(file, resp.Body); err != nil { log.Fatal(err) } fmt.Println("PDF generated successfully") } ``` **Convert HTML to PDF** (HTML must be base64 encoded): ```go package main "bytes" "encoding/base64" "encoding/json" "fmt" "io" "log" "net/http" "os" ) func main() { htmlContent := "Hello!This is a sample PDF." base64Html := base64.StdEncoding.EncodeToString([]byte(htmlContent)) data := map[string]interface{}{ "html": base64Html, "format": "A4", "printBackground": true, "margin": map[string]string{ "top": "30px", "left": "30px", }, } jsonBody, err := json.Marshal(data) if err != nil { log.Fatal(err) } req, err := http.NewRequest("POST", "https://api.pdfbolt.com/v1/direct", bytes.NewReader(jsonBody)) if err != nil { log.Fatal(err) } req.Header.Add("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") req.Header.Add("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { log.Fatal(err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { body, err := io.ReadAll(resp.Body) if err != nil { log.Fatal(err) } fmt.Printf("HTTP %d\n", resp.StatusCode) fmt.Printf("Error Message: %s\n", string(body)) return } file, err := os.Create("document.pdf") if err != nil { log.Fatal(err) } defer file.Close() if _, err := io.Copy(file, resp.Body); err != nil { log.Fatal(err) } fmt.Println("PDF generated successfully") } ``` **Convert a template with data to PDF:** ```go package main "bytes" "encoding/json" "fmt" "io" "log" "net/http" "os" ) func main() { data := map[string]interface{}{ "templateId": "your-template-id", "templateData": map[string]interface{}{ "client_name": "John Doe", "invoice_number": "INV-001", "total_amount": "$299.99", "line_items": []interface{}{ map[string]interface{}{ "description": "Web Development", "unit_price": "$200.00", }, map[string]interface{}{ "description": "Design Services", "unit_price": "$99.99", }, }, }, } jsonBody, err := json.Marshal(data) if err != nil { log.Fatal(err) } req, err := http.NewRequest("POST", "https://api.pdfbolt.com/v1/direct", bytes.NewReader(jsonBody)) if err != nil { log.Fatal(err) } req.Header.Add("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") req.Header.Add("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { log.Fatal(err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { body, err := io.ReadAll(resp.Body) if err != nil { log.Fatal(err) } fmt.Printf("HTTP %d\n", resp.StatusCode) fmt.Printf("Error Message: %s\n", string(body)) return } file, err := os.Create("invoice.pdf") if err != nil { log.Fatal(err) } defer file.Close() if _, err := io.Copy(file, resp.Body); err != nil { log.Fatal(err) } fmt.Println("PDF generated successfully") } ``` :::info New to templates? Create and publish your first template in the [Dashboard Template Designer](/docs/dashboard/templates) or through the [Template API](/docs/api-endpoints/template-api), then use its ID in conversion requests. See the [Template Guide](/docs/pdf-templates) for Handlebars syntax and examples. ::: The **Sync** endpoint returns a JSON response with a download URL for the PDF (valid for 24 hours). **Choose your source:** **Convert a webpage and get a download URL:** ```go package main "bytes" "encoding/json" "fmt" "io" "log" "net/http" ) func main() { data := map[string]interface{}{ "url": "https://example.com", "format": "A4", "printBackground": true, } jsonBody, err := json.Marshal(data) if err != nil { log.Fatal(err) } req, err := http.NewRequest("POST", "https://api.pdfbolt.com/v1/sync", bytes.NewReader(jsonBody)) if err != nil { log.Fatal(err) } req.Header.Add("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") req.Header.Add("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { log.Fatal(err) } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { log.Fatal(err) } if resp.StatusCode != http.StatusOK { fmt.Printf("HTTP %d\n", resp.StatusCode) fmt.Printf("Error Message: %s\n", string(body)) return } var result map[string]interface{} if err := json.Unmarshal(body, &result); err != nil { log.Fatal(err) } fmt.Printf("PDF URL: %s\n", result["documentUrl"]) } ``` **Convert HTML and get a download URL** (HTML must be base64 encoded): ```go package main "bytes" "encoding/base64" "encoding/json" "fmt" "io" "log" "net/http" ) func main() { htmlContent := "Hello!This is a sample PDF." base64Html := base64.StdEncoding.EncodeToString([]byte(htmlContent)) data := map[string]interface{}{ "html": base64Html, "format": "A4", "printBackground": true, "margin": map[string]string{ "top": "30px", "left": "30px", }, } jsonBody, err := json.Marshal(data) if err != nil { log.Fatal(err) } req, err := http.NewRequest("POST", "https://api.pdfbolt.com/v1/sync", bytes.NewReader(jsonBody)) if err != nil { log.Fatal(err) } req.Header.Add("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") req.Header.Add("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { log.Fatal(err) } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { log.Fatal(err) } if resp.StatusCode != http.StatusOK { fmt.Printf("HTTP %d\n", resp.StatusCode) fmt.Printf("Error Message: %s\n", string(body)) return } var result map[string]interface{} if err := json.Unmarshal(body, &result); err != nil { log.Fatal(err) } fmt.Printf("PDF URL: %s\n", result["documentUrl"]) } ``` **Convert a template with data and get a download URL:** ```go package main "bytes" "encoding/json" "fmt" "io" "log" "net/http" ) func main() { data := map[string]interface{}{ "templateId": "your-template-id", "templateData": map[string]interface{}{ "client_name": "John Doe", "invoice_number": "INV-001", "total_amount": "$299.99", "line_items": []interface{}{ map[string]interface{}{ "description": "Web Development", "unit_price": "$200.00", }, map[string]interface{}{ "description": "Design Services", "unit_price": "$99.99", }, }, }, } jsonBody, err := json.Marshal(data) if err != nil { log.Fatal(err) } req, err := http.NewRequest("POST", "https://api.pdfbolt.com/v1/sync", bytes.NewReader(jsonBody)) if err != nil { log.Fatal(err) } req.Header.Add("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") req.Header.Add("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { log.Fatal(err) } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { log.Fatal(err) } if resp.StatusCode != http.StatusOK { fmt.Printf("HTTP %d\n", resp.StatusCode) fmt.Printf("Error Message: %s\n", string(body)) return } var result map[string]interface{} if err := json.Unmarshal(body, &result); err != nil { log.Fatal(err) } fmt.Printf("PDF URL: %s\n", result["documentUrl"]) } ``` :::info New to templates? Create and publish your first template in the [Dashboard Template Designer](/docs/dashboard/templates) or through the [Template API](/docs/api-endpoints/template-api), then use its ID in conversion requests. See the [Template Guide](/docs/pdf-templates) for Handlebars syntax and examples. ::: The **Async** endpoint returns a `requestId` immediately and delivers the final result via webhook callback. **Choose your source:** **Convert a webpage and receive a webhook callback:** ```go package main "bytes" "encoding/json" "fmt" "io" "log" "net/http" ) func main() { data := map[string]interface{}{ "url": "https://example.com", "format": "A4", "printBackground": true, "webhook": "https://your-app.com/webhook", } jsonBody, err := json.Marshal(data) if err != nil { log.Fatal(err) } req, err := http.NewRequest("POST", "https://api.pdfbolt.com/v1/async", bytes.NewReader(jsonBody)) if err != nil { log.Fatal(err) } req.Header.Add("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") req.Header.Add("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { log.Fatal(err) } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { log.Fatal(err) } if resp.StatusCode != http.StatusOK { fmt.Printf("HTTP %d\n", resp.StatusCode) fmt.Printf("Error Message: %s\n", string(body)) return } var result map[string]interface{} if err := json.Unmarshal(body, &result); err != nil { log.Fatal(err) } fmt.Printf("Request ID: %s\n", result["requestId"]) fmt.Println("PDF will be sent to webhook when ready") } ``` **Convert HTML and receive a webhook callback** (HTML must be base64 encoded): ```go package main "bytes" "encoding/base64" "encoding/json" "fmt" "io" "log" "net/http" ) func main() { htmlContent := "Hello!This is a sample PDF." base64Html := base64.StdEncoding.EncodeToString([]byte(htmlContent)) data := map[string]interface{}{ "html": base64Html, "format": "A4", "printBackground": true, "margin": map[string]string{ "top": "30px", "left": "30px", }, "webhook": "https://your-app.com/webhook", } jsonBody, err := json.Marshal(data) if err != nil { log.Fatal(err) } req, err := http.NewRequest("POST", "https://api.pdfbolt.com/v1/async", bytes.NewReader(jsonBody)) if err != nil { log.Fatal(err) } req.Header.Add("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") req.Header.Add("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { log.Fatal(err) } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { log.Fatal(err) } if resp.StatusCode != http.StatusOK { fmt.Printf("HTTP %d\n", resp.StatusCode) fmt.Printf("Error Message: %s\n", string(body)) return } var result map[string]interface{} if err := json.Unmarshal(body, &result); err != nil { log.Fatal(err) } fmt.Printf("Request ID: %s\n", result["requestId"]) fmt.Println("PDF will be sent to webhook when ready") } ``` **Convert a template with data and receive a webhook callback:** ```go package main "bytes" "encoding/json" "fmt" "io" "log" "net/http" ) func main() { data := map[string]interface{}{ "templateId": "your-template-id", "templateData": map[string]interface{}{ "client_name": "John Doe", "invoice_number": "INV-001", "total_amount": "$299.99", "line_items": []interface{}{ map[string]interface{}{ "description": "Web Development", "unit_price": "$200.00", }, map[string]interface{}{ "description": "Design Services", "unit_price": "$99.99", }, }, }, "webhook": "https://your-app.com/webhook", } jsonBody, err := json.Marshal(data) if err != nil { log.Fatal(err) } req, err := http.NewRequest("POST", "https://api.pdfbolt.com/v1/async", bytes.NewReader(jsonBody)) if err != nil { log.Fatal(err) } req.Header.Add("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") req.Header.Add("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { log.Fatal(err) } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { log.Fatal(err) } if resp.StatusCode != http.StatusOK { fmt.Printf("HTTP %d\n", resp.StatusCode) fmt.Printf("Error Message: %s\n", string(body)) return } var result map[string]interface{} if err := json.Unmarshal(body, &result); err != nil { log.Fatal(err) } fmt.Printf("Request ID: %s\n", result["requestId"]) fmt.Println("PDF will be sent to webhook when ready") } ``` :::info New to templates? Create and publish your first template in the [Dashboard Template Designer](/docs/dashboard/templates) or through the [Template API](/docs/api-endpoints/template-api), then use its ID in conversion requests. See the [Template Guide](/docs/pdf-templates) for Handlebars syntax and examples. ::: ## Next Steps :::tip Related reading - [Optimizing HTML for Professional PDF Output](/blog/optimizing-html-for-pdf) – HTML/CSS techniques: page breaks, fonts, and image optimization. - [Compress PDF via API: Reduce File Size Programmatically](/blog/compress-pdf-api) – reduce PDF file size via the PDFBolt API. ::: --- ## Rust PDF Generation: Quick Start Guide # Quick Start for Rust Integrate PDFBolt's REST API in Rust to generate PDFs from URLs, HTML, or templates. The examples below cover all three conversion modes (Direct, Sync, Async). ## 1. Get Your API Key Find your API key on the **API Credentials** page in your Dashboard. If you don't have an account, [sign up](https://app.pdfbolt.com/register) – the free plan includes 100 document conversions per month. ## 2. Make Your First Request Any HTTP client works – adjust the request structure to match your library. Examples use the `reqwest` crate with `tokio` async runtime. Add to your `Cargo.toml`: ```toml [dependencies] reqwest = { version = "0.13", features = ["json"] } serde_json = "1" tokio = { version = "1", features = ["full"] } base64 = "0.22" ``` **Choose your endpoint:** The **Direct** endpoint provides immediate PDF generation and returns the raw PDF file in the response. **Choose your source:** **Convert a webpage to PDF:** ```rust use reqwest::Client; use serde_json::json; use std::fs; #[tokio::main] async fn main() -> Result<(), Box> { let client = Client::new(); let url = "https://api.pdfbolt.com/v1/direct"; let response = client.post(url) .header("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") .header("Content-Type", "application/json") .json(&json!({ "url": "https://example.com", "format": "A4", "printBackground": true })) .send() .await?; if !response.status().is_success() { println!("HTTP {}", response.status().as_u16()); let error_text = response.text().await?; println!("Error Message: {}", error_text); return Ok(()); } let pdf_bytes = response.bytes().await?; fs::write("webpage.pdf", pdf_bytes)?; println!("PDF generated successfully"); Ok(()) } ``` **Convert HTML to PDF** (HTML must be base64 encoded): ```rust use reqwest::Client; use serde_json::json; use base64::{Engine as _, engine::general_purpose}; use std::fs; #[tokio::main] async fn main() -> Result<(), Box> { let client = Client::new(); let url = "https://api.pdfbolt.com/v1/direct"; let html_content = "Hello!This is a sample PDF."; let base64_html = general_purpose::STANDARD.encode(html_content.as_bytes()); let response = client.post(url) .header("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") .header("Content-Type", "application/json") .json(&json!({ "html": base64_html, "format": "A4", "printBackground": true, "margin": { "top": "30px", "left": "30px" } })) .send() .await?; if !response.status().is_success() { println!("HTTP {}", response.status().as_u16()); let error_text = response.text().await?; println!("Error Message: {}", error_text); return Ok(()); } let pdf_bytes = response.bytes().await?; fs::write("document.pdf", pdf_bytes)?; println!("PDF generated successfully"); Ok(()) } ``` **Convert a template with data to PDF:** ```rust use reqwest::Client; use serde_json::json; use std::fs; #[tokio::main] async fn main() -> Result<(), Box> { let client = Client::new(); let url = "https://api.pdfbolt.com/v1/direct"; let response = client.post(url) .header("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") .header("Content-Type", "application/json") .json(&json!({ "templateId": "your-template-id", "templateData": { "client_name": "John Doe", "invoice_number": "INV-001", "total_amount": "$299.99", "line_items": [ {"description": "Web Development", "unit_price": "$200.00"}, {"description": "Design Services", "unit_price": "$99.99"} ] } })) .send() .await?; if !response.status().is_success() { println!("HTTP {}", response.status().as_u16()); let error_text = response.text().await?; println!("Error Message: {}", error_text); return Ok(()); } let pdf_bytes = response.bytes().await?; fs::write("invoice.pdf", pdf_bytes)?; println!("PDF generated successfully"); Ok(()) } ``` :::info New to templates? Create and publish your first template in the [Dashboard Template Designer](/docs/dashboard/templates) or through the [Template API](/docs/api-endpoints/template-api), then use its ID in conversion requests. See the [Template Guide](/docs/pdf-templates) for Handlebars syntax and examples. ::: The **Sync** endpoint returns a JSON response with a download URL for the PDF (valid for 24 hours). **Choose your source:** **Convert a webpage and get a download URL:** ```rust use reqwest::Client; use serde_json::{json, Value}; #[tokio::main] async fn main() -> Result<(), Box> { let client = Client::new(); let url = "https://api.pdfbolt.com/v1/sync"; let response = client.post(url) .header("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") .header("Content-Type", "application/json") .json(&json!({ "url": "https://example.com", "format": "A4", "printBackground": true })) .send() .await?; if !response.status().is_success() { println!("HTTP {}", response.status().as_u16()); let error_text = response.text().await?; println!("Error Message: {}", error_text); return Ok(()); } let result: Value = response.json().await?; println!("PDF URL: {}", result["documentUrl"].as_str().unwrap_or("")); Ok(()) } ``` **Convert HTML and get a download URL** (HTML must be base64 encoded): ```rust use reqwest::Client; use serde_json::{json, Value}; use base64::{Engine as _, engine::general_purpose}; #[tokio::main] async fn main() -> Result<(), Box> { let client = Client::new(); let url = "https://api.pdfbolt.com/v1/sync"; let html_content = "Hello!This is a sample PDF."; let base64_html = general_purpose::STANDARD.encode(html_content.as_bytes()); let response = client.post(url) .header("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") .header("Content-Type", "application/json") .json(&json!({ "html": base64_html, "format": "A4", "printBackground": true, "margin": { "top": "30px", "left": "30px" } })) .send() .await?; if !response.status().is_success() { println!("HTTP {}", response.status().as_u16()); let error_text = response.text().await?; println!("Error Message: {}", error_text); return Ok(()); } let result: Value = response.json().await?; println!("PDF URL: {}", result["documentUrl"].as_str().unwrap_or("")); Ok(()) } ``` **Convert a template with data and get a download URL:** ```rust use reqwest::Client; use serde_json::{json, Value}; #[tokio::main] async fn main() -> Result<(), Box> { let client = Client::new(); let url = "https://api.pdfbolt.com/v1/sync"; let response = client.post(url) .header("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") .header("Content-Type", "application/json") .json(&json!({ "templateId": "your-template-id", "templateData": { "client_name": "John Doe", "invoice_number": "INV-001", "total_amount": "$299.99", "line_items": [ {"description": "Web Development", "unit_price": "$200.00"}, {"description": "Design Services", "unit_price": "$99.99"} ] } })) .send() .await?; if !response.status().is_success() { println!("HTTP {}", response.status().as_u16()); let error_text = response.text().await?; println!("Error Message: {}", error_text); return Ok(()); } let result: Value = response.json().await?; println!("PDF URL: {}", result["documentUrl"].as_str().unwrap_or("")); Ok(()) } ``` :::info New to templates? Create and publish your first template in the [Dashboard Template Designer](/docs/dashboard/templates) or through the [Template API](/docs/api-endpoints/template-api), then use its ID in conversion requests. See the [Template Guide](/docs/pdf-templates) for Handlebars syntax and examples. ::: The **Async** endpoint returns a `requestId` immediately and delivers the final result via webhook callback. **Choose your source:** **Convert a webpage and receive a webhook callback:** ```rust use reqwest::Client; use serde_json::{json, Value}; #[tokio::main] async fn main() -> Result<(), Box> { let client = Client::new(); let url = "https://api.pdfbolt.com/v1/async"; let response = client.post(url) .header("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") .header("Content-Type", "application/json") .json(&json!({ "url": "https://example.com", "format": "A4", "printBackground": true, "webhook": "https://your-app.com/webhook" })) .send() .await?; if !response.status().is_success() { println!("HTTP {}", response.status().as_u16()); let error_text = response.text().await?; println!("Error Message: {}", error_text); return Ok(()); } let result: Value = response.json().await?; println!("Request ID: {}", result["requestId"].as_str().unwrap_or("")); println!("PDF will be sent to webhook when ready"); Ok(()) } ``` **Convert HTML and receive a webhook callback** (HTML must be base64 encoded): ```rust use reqwest::Client; use serde_json::{json, Value}; use base64::{Engine as _, engine::general_purpose}; #[tokio::main] async fn main() -> Result<(), Box> { let client = Client::new(); let url = "https://api.pdfbolt.com/v1/async"; let html_content = "Hello!This is a sample PDF."; let base64_html = general_purpose::STANDARD.encode(html_content.as_bytes()); let response = client.post(url) .header("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") .header("Content-Type", "application/json") .json(&json!({ "html": base64_html, "format": "A4", "printBackground": true, "margin": { "top": "30px", "left": "30px" }, "webhook": "https://your-app.com/webhook" })) .send() .await?; if !response.status().is_success() { println!("HTTP {}", response.status().as_u16()); let error_text = response.text().await?; println!("Error Message: {}", error_text); return Ok(()); } let result: Value = response.json().await?; println!("Request ID: {}", result["requestId"].as_str().unwrap_or("")); println!("PDF will be sent to webhook when ready"); Ok(()) } ``` **Convert a template with data and receive a webhook callback:** ```rust use reqwest::Client; use serde_json::{json, Value}; #[tokio::main] async fn main() -> Result<(), Box> { let client = Client::new(); let url = "https://api.pdfbolt.com/v1/async"; let response = client.post(url) .header("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") .header("Content-Type", "application/json") .json(&json!({ "templateId": "your-template-id", "templateData": { "client_name": "John Doe", "invoice_number": "INV-001", "total_amount": "$299.99", "line_items": [ {"description": "Web Development", "unit_price": "$200.00"}, {"description": "Design Services", "unit_price": "$99.99"} ] }, "webhook": "https://your-app.com/webhook" })) .send() .await?; if !response.status().is_success() { println!("HTTP {}", response.status().as_u16()); let error_text = response.text().await?; println!("Error Message: {}", error_text); return Ok(()); } let result: Value = response.json().await?; println!("Request ID: {}", result["requestId"].as_str().unwrap_or("")); println!("PDF will be sent to webhook when ready"); Ok(()) } ``` :::info New to templates? Create and publish your first template in the [Dashboard Template Designer](/docs/dashboard/templates) or through the [Template API](/docs/api-endpoints/template-api), then use its ID in conversion requests. See the [Template Guide](/docs/pdf-templates) for Handlebars syntax and examples. ::: ## Next Steps :::tip Related reading - [Optimizing HTML for Professional PDF Output](/blog/optimizing-html-for-pdf) – HTML/CSS techniques: page breaks, fonts, and image optimization. - [Compress PDF via API: Reduce File Size Programmatically](/blog/compress-pdf-api) – reduce PDF file size via the PDFBolt API. ::: --- ## Postman # Quick Start with Postman Test the PDFBolt REST API in Postman without writing code. Open the **quick start video** on YouTube, use our **Postman collection**, or follow the step-by-step guide below. ## Quick API Testing Fork our **Postman collection** with one click to test all PDFBolt API endpoints. Set your **API key** as an environment variable, then start sending requests. [](https://app.getpostman.com/run-collection/40399365-9472b2d4-c8da-4338-8774-962cc6bb9347?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D40399365-9472b2d4-c8da-4338-8774-962cc6bb9347%26entityType%3Dcollection%26workspaceId%3D3a6b1d25-d352-4c2e-8a9b-0b4fcb6d6cae#?env%5BPDFBolt%5D=W3sia2V5IjoiYmFzZV91cmwiLCJ2YWx1ZSI6Imh0dHBzOi8vYXBpLnBkZmJvbHQuY29tIiwiZW5hYmxlZCI6dHJ1ZSwidHlwZSI6ImRlZmF1bHQiLCJzZXNzaW9uVmFsdWUiOiJodHRwczovL2FwaS5wZGZib2x0LmNvbSIsImNvbXBsZXRlU2Vzc2lvblZhbHVlIjoiaHR0cHM6Ly9hcGkucGRmYm9sdC5jb20iLCJzZXNzaW9uSW5kZXgiOjB9LHsia2V5IjoiQVBJX0tFWSIsInZhbHVlIjoiIiwiZW5hYmxlZCI6dHJ1ZSwidHlwZSI6InNlY3JldCIsInNlc3Npb25WYWx1ZSI6IiIsImNvbXBsZXRlU2Vzc2lvblZhbHVlIjoiIiwic2Vzc2lvbkluZGV4IjoxfSx7ImtleSI6IndlYmhvb2tfdXJsIiwidmFsdWUiOiIiLCJlbmFibGVkIjp0cnVlLCJ0eXBlIjoiZGVmYXVsdCIsInNlc3Npb25WYWx1ZSI6IiIsImNvbXBsZXRlU2Vzc2lvblZhbHVlIjoiIiwic2Vzc2lvbkluZGV4IjoyfSx7ImtleSI6ImN1c3RvbVMzX3VybCIsInZhbHVlIjoiIiwiZW5hYmxlZCI6dHJ1ZSwidHlwZSI6ImRlZmF1bHQiLCJzZXNzaW9uVmFsdWUiOiIiLCJjb21wbGV0ZVNlc3Npb25WYWx1ZSI6IiIsInNlc3Npb25JbmRleCI6M31d) :::tip Quick start video Prefer a video walkthrough? Watch the [Postman quick start video on YouTube](https://www.youtube.com/watch?v=s9LBZFEYt6g). ::: ## Quick Start from Scratch ### 1. Get Your API Key Find your API key on the **API Credentials** page in your Dashboard. If you don't have an account, [sign up](https://app.pdfbolt.com/register) – the free plan includes 100 document conversions per month. ### 2. Set Up Postman for PDFBolt #### 1. Create a New Request - Open Postman and create a new Request. - Set the HTTP Method to `POST`. - Enter one of the following PDFBolt API endpoint URLs based on your use case: ```text https://api.pdfbolt.com/v1/direct ``` ```text https://api.pdfbolt.com/v1/sync ``` ```text https://api.pdfbolt.com/v1/async ``` #### 2. Set Up Authorization - Go to the **Authorization** tab. - Choose **API Key** as the Auth Type. - Enter the following values: - Key: `API-KEY` - Value: Your unique API key. - Select *Header* to include the key in the request headers.
**Setting Up Your API Key as an Environment Variable in Postman** Instead of manually entering your API key every time, store it as an **environment variable** in Postman for easier reuse in future requests. #### **How to set it up in Postman:** 1. Open Postman and go to **Environments**. 2. Click ➕ to create a new environment. 3. Create a new variable called `API_KEY` and set its type to *Secret*. 4. Paste your **API key** into Initial value and Current value, then save the environment. 5. In your request headers, set the key name to `API-KEY` and the value to `{{API_KEY}}`. Postman will substitute `{{API_KEY}}` with your actual API key when sending requests.
#### 3. Define the Request Body - Navigate to the **Body** tab. - Select raw and set the content type to `JSON`. - Choose your source: **Convert a webpage to PDF:** ```json { "url": "https://example.com", "format": "A4", "printBackground": true } ``` **Convert HTML to PDF** (HTML must be base64 encoded): ```json { "html": "PGh0bWw+PGJvZHk+PGgxPkhlbGxvITwvaDE+PHA+VGhpcyBpcyBhIHNhbXBsZSBQREYuPC9wPjwvYm9keT48L2h0bWw+", "format": "A4", "printBackground": true, "margin": { "top": "30px", "left": "30px" } } ``` :::note Base64 Encoded HTML The above base64 string represents: ```html Hello!This is a sample PDF. ``` ::: **Convert a template with data to PDF:** ```json { "templateId": "your-template-id", "templateData": { "client_name": "John Doe", "invoice_number": "INV-001", "total_amount": "$299.99", "line_items": [ {"description": "Web Development", "unit_price": "$200.00"}, {"description": "Design Services", "unit_price": "$99.99"} ] } } ``` :::info New to templates? Create and publish your first template in the [Dashboard Template Designer](/docs/dashboard/templates) or through the [Template API](/docs/api-endpoints/template-api), then use its ID in conversion requests. See the [Template Guide](/docs/pdf-templates) for Handlebars syntax and examples. ::: #### 4. Send the Request - Click *Send* to execute the request. - Upon success, the response depends on the endpoint you chose: :::info Expected response - **Direct** – returns the binary PDF (use *Save Response → Save to file* in Postman to save it). - **Sync** – returns JSON with `documentUrl` (the PDF download link, valid for 24 hours). - **Async** – returns JSON with `requestId`. The PDF is delivered to your `webhook` URL when ready. ::: :::note Async endpoint If you want to use the `/async` endpoint, include the [`webhook`](/docs/api-endpoints/async) parameter in your request body. ::: ## Next Steps --- ## Automation Platform Integrations Integrate PDFBolt with automation platforms to generate invoices, reports, certificates, and other documents using visual workflows. ## Integration Guides | **Platform** | **Description** | **Best For** | |--------------|---------------------------------------------------------------------------------------|----------------------------------------------------------| | **n8n** | Open-source workflow automation with a dedicated PDFBolt community node and visual builder. | Complex workflows, self-hosted solutions, developers who want flexibility. | | **Make** | Visual automation platform for connecting apps and building multi-step scenarios. | Users needing flexible automation from simple to complex workflows. | | **Zapier** | Automation platform with a large app library and quick setup. | Non-technical users, small teams, simple mainstream app integrations. | | **Airtable** | Spreadsheet-database hybrid with built-in automation capabilities. | Database-driven documents, team collaboration. | | **Bubble** | Visual programming platform for building web applications. | Custom web apps, user-triggered documents. | | **Integrately** | One-click automation platform with pre-built workflows. | Fast setup, simple automations. | ## Why Use Automation Platforms? These integrations enable you to: - **Visual Workflow Building**: Design workflows using drag-and-drop interfaces. - **Connect Multiple Services**: Trigger PDF generation from forms, databases, webhooks, schedules, and various apps. - **Minimal Setup**: Automate document creation with visual workflows and simple code snippets. - **Faster Deployment**: Launch automations in hours instead of weeks. - **Simplify Maintenance**: Visual workflows are easier to update and troubleshoot. ## Common Use Cases - **E-commerce**: Automatically generate invoices when orders are placed. - **Education**: Create certificates when students complete courses. - **HR & Onboarding**: Generate employee handbooks and welcome packets. - **Reporting**: Schedule automated report generation and distribution. - **Event Management**: Create tickets and badges for registered attendees. - **Sales**: Generate proposals and quotes from CRM data. --- ## n8n Integration Guide Learn how to integrate PDFBolt with n8n to automate PDF generation in your workflows. Generate professional documents from templates, HTML content, or web pages using n8n's visual workflow builder. ## Choose Your Integration Method | Method | Best For | Setup | |--------|----------|-------| | [PDFBolt Community Node](/docs/automation-platform-integrations/n8n-integration-guide/pdfbolt-community-node) | **Recommended** for most users – visual UI, no Base64 encoding or JSON to write | Install node, add API key, select operation | | [HTTP Request Node](/docs/automation-platform-integrations/n8n-integration-guide/http-request-node) | Full control over the raw API request | Configure headers, Base64 encode HTML in a previous step, compose JSON body | ### PDFBolt Community Node (Recommended) The dedicated PDFBolt community node lets you configure and run PDFBolt conversions directly in n8n through a visual interface. No manual header configuration or Base64 encoding is required – just select an operation, fill in the fields, and run the workflow. :::info Learn more See the [PDFBolt Community Node guide](/docs/automation-platform-integrations/n8n-integration-guide/pdfbolt-community-node) for setup and usage details. ::: ### HTTP Request Node You can also call the PDFBolt API using n8n's built-in HTTP Request node. This works without installing anything and gives you direct control over the request body. :::info Learn more See the [HTTP Request Node guide](/docs/automation-platform-integrations/n8n-integration-guide/http-request-node) for setup and usage details. ::: ## Prerequisites Before starting with either method, ensure you have: 1. **PDFBolt API Key** - [Sign up](https://app.pdfbolt.com/register) or [log in](https://app.pdfbolt.com/login) to your PDFBolt account. - Navigate to the API Credentials section. - Copy your API key for authentication. 2. **n8n Access** - Active n8n cloud account or running self-hosted instance. ## Additional Resources n8n Resources - n8n Documentation - n8n Community Forum - n8n Tutorials --- ## n8n: PDFBolt Community Node Use the PDFBolt community node to generate PDFs directly from n8n – no manual API configuration, no Base64 encoding, no JSON body to write. Select an operation, fill in the fields, and run. ## Prerequisites Before starting, ensure you have: 1. **PDFBolt API Key** - [Sign up](https://app.pdfbolt.com/register) or [log in](https://app.pdfbolt.com/login) to your PDFBolt account. - Navigate to the API Credentials section. - Copy your API key for authentication. 2. **n8n Access** - Active n8n cloud account or running self-hosted instance. ## Install the PDFBolt Node ### n8n Cloud The PDFBolt node is available as a verified community node. Search for **PDFBolt** in the nodes panel and add it to your workflow. ### Self-Hosted n8n 1. Go to **Settings** > **Community Nodes**. 2. Click **Install**. 3. Enter `n8n-nodes-pdfbolt`. 4. Click **Install**. ## Set Up Credentials 1. Add a **PDFBolt** node to your workflow. 2. Under **Credential**, click **Set up credential**. 3. Paste your **API Key**. 4. Click **Save**. 5. If the API key is valid, you will see **Connection tested successfully**. ## Choose Your Endpoint Select the endpoint based on your workflow needs: | Endpoint | Best For | Returns | |----------|----------|---------| | **Direct** | Immediate PDF delivery | PDF file in the response | | **Sync** | URL-based access | JSON with download URL (valid for 24 hours) | | **Async** | Background processing | Webhook callback with results (paid plans only) | :::info Endpoint Details See [API Endpoints](/docs/api-endpoints) for detailed specifications. ::: ## Choose Your Source Select the content source that best fits your use case: | Source | Best For | When to Use | |--------|----------|-------------| | **Templates** | Recurring documents with consistent layouts | Invoices, contracts, certificates – any document you generate repeatedly with different data | | **HTML** | Custom documents | When you need full control over a unique layout | | **URL** | Existing web pages | Archiving documentation, reports, capturing dashboards, saving public web pages | :::info Source Parameters Learn more about source parameters in the [API Documentation](/docs/parameters#source-parameters). ::: ## Source 1: Templates Templates separate design from data – you create the layout once, then send different data each time to generate a new PDF. ### How It Works 1. **Create your template** using one of these options: - Build a custom layout with HTML, CSS, and Handlebars variables in the [Dashboard Template Designer](/docs/dashboard/templates). - Start from a ready-made design in the [template gallery](/pdf-templates). - [Generate a draft with AI](/docs/ai-pdf-template-generation) from a description or reference file. - Create and manage the template programmatically through the [Template API](/docs/api-endpoints/template-api). 2. **Publish the template** to get your unique `templateId`. 3. **Configure the PDFBolt node** with the `templateId` and your data in `templateData`. 4. **Run the workflow** – PDFBolt merges the data with your template and returns the PDF. ### Example: Invoice Generation **Real-world scenario:** A customer places an order in your e-commerce system. Automatically generate a professional invoice PDF and send it to the customer. **PDFBolt Node Configuration:** 1. Set **Operation** to `Convert Template to PDF`. 2. Set **Endpoint** to `Direct`. 3. Enter your **Template ID**. 4. Enter your **Template Data**:
**Sample Template Data** ```json { "invoice_number": "INV-2025-001", "client_name": "John Doe", "line_items": [ { "quantity": 1, "tax_rate": 10, "unit_price": "1200.00", "description": "Website Design", "total_amount": "1200.00" }, { "quantity": 2, "tax_rate": 10, "unit_price": "250.00", "description": "Logo Design Package", "total_amount": "500.00" } ], "total_amount": "1700.00" } ```
5. Click **Add Option** to set additional PDF parameters like format, margins, or header/footer (optional). :::tip Mapping Data from Previous Nodes When data comes from a previous node (webhook, database, form), switch the Template Data field to **Expression** mode and use: - `{{ JSON.stringify($json) }}` to pass the entire object from the previous node. - `{{ JSON.stringify({ invoice_number: $json.invoice_no, line_items: $json.items }) }}` to pick specific fields. - `{"invoice_number": "{{ $json.invoice_no }}"}` for simple string fields (does not work for arrays or objects). Field names in Template Data must match the variable names in your template (e.g. `invoice_number`, `line_items`). ::: ### Template Workflow Example **Complete workflow:** 1. **Webhook** – Receives order data from your system. 2. **PDFBolt** – Generates invoice PDF. 3. **Send Message** – Sends PDF to customer. ## Source 2: HTML Convert HTML directly into PDFs. Paste your raw HTML into the node – the node handles Base64 encoding automatically. ### How It Works 1. Paste HTML content into the **HTML** field. 2. The node Base64-encodes it before sending. 3. PDFBolt renders the HTML and returns the PDF. :::info No Base64 Encoding Needed With the [HTTP Request method](/docs/automation-platform-integrations/n8n-integration-guide/http-request-node), HTML must be Base64-encoded before sending. The community node handles this automatically – just **paste raw HTML**. ::: ### Example: Event Program **Real-world scenario:** You're organizing a webinar or conference. Generate a PDF program with the schedule, speakers, and session details that attendees can download. **PDFBolt Node Configuration:** 1. Set **Operation** to `Convert HTML to PDF`. 2. Set **Endpoint** to `Direct`. 3. Paste your HTML into the **HTML** field. 4. Click **Add Option** and set: - **Format** = `A4` - **Print Background** = `true` - **Margin Top** = `30px` - **Margin Right** = `20px` - **Margin Bottom** = `30px` - **Margin Left** = `20px` :::tip PDF Customization Use **Additional Options** to adjust format, margins, backgrounds, and other settings without modifying your HTML. See [Conversion Parameters](/docs/parameters) for all available options. :::
**View Complete HTML Example** ```html Tech Summit 2026 Building the Future Together September 18, 2026 | Virtual Event Event Details: Date: September 18, 2026 Time: 9:00 AM - 5:00 PM EST Platform: Zoom (link will be sent via email) Event Schedule 9:00 AM - 10:30 AM Opening Keynote: The Future of AI Speaker: Dr. Sarah Johnson, AI Research Director Exploring emerging trends in artificial intelligence and their impact on business. 10:45 AM - 12:30 PM Workshop: Building Scalable APIs Speaker: Mike Chen, Senior Engineer Hands-on session covering best practices for API design and implementation. Coffee Break (12:30 PM - 1:00 PM) 1:00 PM - 2:45 PM Panel Discussion: Cloud Architecture Panelists: Various industry experts Interactive discussion about modern cloud infrastructure and DevOps practices. 3:00 PM - 5:00 PM Security in Production: Lessons Learned Speaker: Anna Torres, Head of Security Engineering Real-world case studies on securing applications at scale and incident response. Questions? Contact us at events@example.com ```
### HTML Workflow Example **Complete workflow:** 1. **Typeform Trigger** – New registration form submitted. 2. **PDFBolt** – Generates event program PDF from HTML. 3. **Gmail** – Sends confirmation email with PDF program to the attendee. ## Source 3: URL Generate PDFs from any publicly accessible HTTPS webpage – useful for archiving web content, capturing dashboards, or generating reports from live pages. ### How It Works 1. Enter the target URL into the **URL** field. 2. PDFBolt loads and renders the page. 3. Returns a PDF of the rendered page. ### Example: Daily Dashboard Report **Real-world scenario:** Your team uses an analytics dashboard. Every morning, capture the dashboard as a PDF and share it on Slack. **PDFBolt Node Configuration:** 1. Set **Operation** to `Convert URL to PDF`. 2. Set **Endpoint** to `Direct`. 3. Enter the URL: `https://analytics.yourcompany.com/dashboard` 4. Click **Add Option** and set: - **Format** = `A4` - **Print Background** = `true` - **Landscape** = `true` - **Wait Until** = `Network Idle (No Requests for 500ms)` :::tip Wait for Page Load Use `Network Idle` to ensure all dashboard charts and data finish loading before generating the PDF. For pages with specific loading indicators, use the `Wait For Selector` option instead. ::: ### URL Workflow Example **Complete workflow:** 1. **Schedule Trigger** – Every weekday at 8:00 AM. 2. **PDFBolt** – Captures dashboard as PDF. 3. **Slack** – Shares report with team. 4. **Google Drive** – Archives PDF. ## Additional Options The PDFBolt node includes 30+ parameters. Click **Add Option** to access them: | Group | Options | |-------|---------| | **Page Layout** | Format, orientation, dimensions, margins, scale, page ranges | | **Output** | Filename, compression, content disposition | | **Header & Footer** | Custom HTML templates with page numbers, title, date, URL | | **Rendering** | Background graphics, media type, JavaScript, timeout, wait conditions | | **Viewport & Device** | Viewport size, mobile emulation, device scale factor | | **HTTP** | Cookies, custom headers, basic authentication | | **Print Production** | PDF/X standards, color space (RGB/CMYK), ICC profiles | :::info Full Parameter Reference See [Conversion Parameters](/docs/parameters) for detailed descriptions of all available options. ::: ## Additional Resources ### PDFBolt Documentation - [Template Management Guide](/docs/dashboard/templates) – Create and manage templates. - [Templates Overview](/docs/pdf-templates) – Template concepts, benefits, and use cases. - [API Endpoints](/docs/api-endpoints) – All available endpoints. - [Conversion Parameters](/docs/parameters) – Complete parameter reference. - [Error Handling](/docs/error-handling) – Error codes and solutions. - [S3 Bucket Upload](/docs/s3-bucket-upload) – Store PDFs permanently. - [n8n Invoice Automation](/blog/n8n-invoice-automation) – Generate and send PDF invoices from Stripe or Shopify. ### n8n Resources - n8n Documentation - n8n Community Forum - PDFBolt Node on npm --- ## n8n: PDFBolt via HTTP Request Node Learn how to use n8n's built-in HTTP Request node to call the PDFBolt API directly. This method works without installing any community nodes. :::tip Recommended: PDFBolt Community Node For a simpler setup, use the [PDFBolt Community Node](/docs/automation-platform-integrations/n8n-integration-guide/pdfbolt-community-node) – no manual header configuration, no Base64 encoding, and a visual UI for all parameters. ::: ## Prerequisites Before starting, ensure you have: 1. **PDFBolt API Key** - [Sign up](https://app.pdfbolt.com/register) or [log in](https://app.pdfbolt.com/login) to your PDFBolt account. - Navigate to the API Credentials section. - Copy your API key for authentication. 2. **n8n Access** - Active n8n cloud account or running self-hosted instance. ## Basic Setup ### Configure HTTP Request Node Use n8n's HTTP Request node to call the PDFBolt API: 1. Add an **HTTP Request** node to your workflow. 2. Configure authentication: - **Method:** `POST` - **URL:** `https://api.pdfbolt.com/v1/direct` - **Authentication:** `Generic Credential Type` - **Generic Auth Type:** `Header Auth` 3. Create authentication credential: - **Name:** `API-KEY` - **Value:** `XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX` 4. Configure request body: - **Send Body:** `Enabled` - **Body Content Type:** `JSON` - **Specify Body:** `Using JSON` ### Choose Your Endpoint Select the PDFBolt endpoint based on your workflow needs: | Endpoint | Best For | Returns | |----------|----------|---------| | `/v1/direct` | Immediate PDF delivery | Raw PDF data in response | | `/v1/sync` | URL-based access | JSON with download URL (valid for 24 hours) | | `/v1/async` | Background processing | Webhook callback with results (paid plans only) | :::info Endpoint Details See [API Endpoints](/docs/api-endpoints) for detailed specifications. ::: ### Choose Your Source Select the content source that best fits your use case: | Source | Best For | When to Use | |--------|----------|-------------| | **Templates** | Recurring documents with consistent layouts | Invoices, contracts, certificates – any document you generate repeatedly with different data | | **HTML** | Custom documents | When you need full control over a unique layout | | **URL** | Existing web pages | Archiving documentation, reports, capturing dashboards, saving public web pages | :::info Source Parameters Learn more about source parameters in the [API Documentation](/docs/parameters#source-parameters). ::: ## Source 1: Templates Templates provide the most efficient way to generate consistent, branded PDFs by separating design from data. ### How It Works 1. **Create your template** using one of these options: - Build a custom layout with HTML, CSS, and Handlebars variables in the [Dashboard Template Designer](/docs/dashboard/templates). - Start from a ready-made design in the [template gallery](/pdf-templates). - [Generate a draft with AI](/docs/ai-pdf-template-generation) from a description or reference file. - Create and manage the template programmatically through the [Template API](/docs/api-endpoints/template-api). 2. **Publish the template** to make it available via API and get your unique `templateId`. 3. **Send API request** with only `templateId` and `templateData`. 4. **Receive your PDF** – PDFBolt merges the data with your template and returns the generated PDF. ### Example: Invoice Generation **Real-world scenario:** A customer places an order in your e-commerce system. Automatically generate a professional invoice PDF and send it to the customer. **n8n HTTP Request Configuration:** If your workflow is triggered by a webhook, paste this expression into the **JSON body** field of the HTTP Request node: ```json {{ $json.body.toJsonString() }} ``` :::info How It Works - `$json.body` – Accesses the data received from the webhook. - `toJsonString()` – Converts the object into JSON string format. - Result: PDFBolt receives the data in the correct structure for template processing. ::: Expected example webhook payload: ```json { "templateId": "your-template-id", "templateData": { "invoice_number": "INV-2025-001", "client_name": "John Doe", "line_items": [ { "quantity": 1, "tax_rate": 10, "unit_price": "1200.00", "description": "Website Design", "total_amount": "1200.00" }, { "quantity": 2, "tax_rate": 10, "unit_price": "250.00", "description": "Logo Design Package", "total_amount": "500.00" } ], "total_amount": "1700.00" } } ``` When your data comes from previous workflow nodes (like databases, forms, or APIs), map the fields manually using n8n expressions: ```json { "templateId": "your-template-id", "templateData": { "invoice_number": "{{ $json.invoice_number }}", "client_name": "{{ $json.client_name }}", "line_items": {{ $json.line_items.toJsonString() }}, "total_amount": "{{ $json.total_amount }}" } } ``` :::tip Expression Syntax - Use `{{ $json.fieldName }}` to reference simple fields from the previous node. - Use `.toJsonString()` for arrays and objects to maintain proper JSON structure. - All field names must match your template's Handlebars variables. ::: ### Template Workflow Example **Complete workflow:** 1. **Webhook** – Receives order data from your system. 2. **HTTP Request (PDFBolt)** – Generates invoice PDF. 3. **Send Message** – Sends PDF to customer. :::tip Data Mapping Field names in `templateData` must exactly match Handlebars variables in your template. Mismatched names result in empty values. ::: ## Source 2: HTML Convert HTML directly into PDFs – useful for custom documents, event programs, announcements, or guides. ### How It Works 1. Prepare HTML content in your workflow. 2. Base64 encode the HTML. 3. Send **encoded HTML to PDFBolt**. 4. Receive generated PDF. ### Example: Event Program **Real-world scenario:** You're organizing a webinar or conference and want attendees to download a PDF program with schedule, speakers, and session details. 1. Add a **Code** node after your event registration trigger:
**View Complete Code Example** ```javascript // Static event program HTML – same for all attendees const htmlContent = ` Tech Summit 2026 Building the Future Together September 18, 2026 | Virtual Event Event Details: Date: September 18, 2026 Time: 9:00 AM - 5:00 PM EST Platform: Zoom (link will be sent via email) Event Schedule 9:00 AM - 10:30 AM Opening Keynote: The Future of AI Speaker: Dr. Sarah Johnson, AI Research Director Exploring emerging trends in artificial intelligence and their impact on business. 10:45 AM - 12:30 PM Workshop: Building Scalable APIs Speaker: Mike Chen, Senior Engineer Hands-on session covering best practices for API design and implementation. Lunch Break (12:30 PM - 1:00 PM) 1:00 PM - 2:45 PM Panel Discussion: Cloud Architecture Panelists: Various industry experts Interactive discussion about modern cloud infrastructure and DevOps practices. 3:00 PM - 5:00 PM Security in Production: Lessons Learned Speaker: Anna Torres, Head of Security Engineering Real-world case studies on securing applications at scale and incident response. Questions? Contact us at events@example.com `; // Encode to base64 const base64Html = Buffer.from(htmlContent).toString('base64'); return [{ json: { html: base64Html } }]; ```
:::warning HTML Encoding HTML must be Base64 encoded before sending to PDFBolt. Use `Buffer.from(html).toString('base64')` in Code nodes. ::: 2. In the **HTTP Request** node body, configure PDF settings: ```json { "html": "{{ $json.html }}", "format": "A4", "printBackground": true, "margin": { "top": "30px", "bottom": "30px", "right": "30px", "left": "30px" } } ``` ### HTML Workflow Example **Complete workflow:** 1. **Typeform Trigger** – New registration form submitted. 2. **Code Node** – Generate event program (Base64-encoded HTML content). 3. **HTTP Request (PDFBolt)** – Generate a PDF. 4. **Gmail** – Send confirmation email with PDF program to the attendee. :::tip PDF Customization By separating PDF settings into the HTTP Request body, you can adjust format, margins, and other options without modifying the HTML code. See [Conversion Parameters](/docs/parameters) for all available options. ::: ## Source 3: URL Generate PDFs from any publicly accessible HTTPS webpage – useful for archiving web content, capturing dashboards, or generating reports. ### How It Works 1. Provide the target URL. 2. PDFBolt loads and renders the page. 3. Returns a PDF of the rendered page. ### Example: Daily Dashboard Report **Real-world scenario:** Your team uses an analytics dashboard. Every morning, automatically capture the dashboard as PDF and share it on Slack so the team stays informed. In the **HTTP Request** node body, configure the URL and PDF settings: ```json { "url": "https://analytics.yourcompany.com/dashboard", "format": "A4", "landscape": true, "printBackground": true, "waitUntil": "networkidle", "margin": { "top": "20px", "right": "20px", "bottom": "20px", "left": "20px" } } ``` :::tip Wait for Page Load Use `waitUntil: "networkidle"` to ensure all dashboard charts and data finish loading before capturing the PDF. For pages with specific loading indicators, use `waitForSelector` instead. ::: ### URL Workflow Example **Complete workflow:** 1. **Schedule Trigger** – Every weekday at 8:00 AM. 2. **HTTP Request (PDFBolt)** – Capture dashboard as PDF. 3. **Slack** – Shares report with team. 4. **Google Drive** – Archives PDF. ## Additional Resources ### PDFBolt Documentation - [Template Management Guide](/docs/dashboard/templates) – Detailed creation and management instructions. - [Templates Overview](/docs/pdf-templates) – Templates overview, benefits, and use cases. - [API Endpoints](/docs/api-endpoints) – All available endpoints. - [Conversion Parameters](/docs/parameters) – Complete parameter reference. - [Error Handling](/docs/error-handling) – Error codes and solutions. - [S3 Bucket Upload](/docs/s3-bucket-upload) – Store PDFs permanently. - [Automate PDF Generation in n8n](/blog/n8n-pdf-automation-guide) – Step-by-step invoice workflow using the HTTP Request node. ### n8n Resources - n8n Documentation - n8n Community Forum --- ## Make Integration Guide Learn how to integrate PDFBolt with Make to automate PDF generation in your scenarios. Generate professional documents from templates, HTML content, or web pages using Make's visual automation platform. ## Prerequisites Before starting, ensure you have: 1. **PDFBolt API Key** - [Sign up](https://app.pdfbolt.com/register) or [log in](https://app.pdfbolt.com/login) to your PDFBolt account. - Navigate to the API Credentials section. - Copy your API key for authentication. 2. **Make Account** - Active Make account (free or paid plan). ## Basic Setup ### Configure HTTP Module Use Make's HTTP module to call the PDFBolt API: 1. Add an **HTTP** module to your scenario (select "Make a request"). 2. Configure authentication: - **URL:** `https://api.pdfbolt.com/v1/direct` - **Method:** `POST` - **Headers:** - Name: `API-KEY` - Value: `XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX` 3. Set up request body: - **Body type:** `Raw` - **Content type:** `JSON (application/json)` ### Choose Your Endpoint Select the PDFBolt endpoint based on your scenario needs: | Endpoint | Best For | Returns | |----------|----------|---------| | `/v1/direct` | Immediate PDF delivery | Raw PDF data in response | | `/v1/sync` | URL-based access | JSON with download URL (valid for 24 hours) | | `/v1/async` | Background processing | Webhook callback with results (paid plans only) | :::info Endpoint Details See [API Endpoints](/docs/api-endpoints) for detailed specifications. ::: ### Choose Your Source Select the content source that best fits your use case: | Source | Best For | When to Use | |--------|----------|-------------| | **Templates** | Recurring documents with consistent layouts | Invoices, contracts, certificates – any document you generate repeatedly with different data | | **HTML** | Custom documents | When you need full control over a unique layout | | **URL** | Existing web pages | Archiving documentation, reports, capturing dashboards, saving public web pages | :::info Source Parameters Learn more about source parameters in the [API Documentation](/docs/parameters#source-parameters). ::: ## Source 1: Templates Templates provide the most efficient way to generate consistent, branded PDFs by separating design from data. ### How It Works 1. **Create your template** using one of these options: - Build a custom layout with HTML, CSS, and Handlebars variables in the [Dashboard Template Designer](/docs/dashboard/templates). - Start from a ready-made design in the [template gallery](/pdf-templates). - [Generate a draft with AI](/docs/ai-pdf-template-generation) from a description or reference file. - Create and manage the template programmatically through the [Template API](/docs/api-endpoints/template-api). 2. **Publish the template** to make it available via API and get your unique `templateId`. 3. **Send API request** with only `templateId` and `templateData`. 4. **Receive your PDF** – PDFBolt returns the generated PDF. ### Example: Invoice Generation **Real-world scenario:** A customer places an order in your e-commerce system. Automatically generate a professional invoice PDF and send it to the customer. 1. Configure the **HTTP – PDFBolt** module: When receiving order data from a webhook trigger, configure the Request content field: ```json { "templateId": "your-template-id", "templateData": { "invoice_number": "{{1.invoice_number}}", "issue_date": "{{1.order_date}}", "client_name": "{{1.customer_name}}", "client_email": "{{1.customer_email}}", "line_items": [{{1.line_items}}], "total_amount": "{{1.total}}" } } ``` :::info How It Works - `{{1.fieldName}}` references data from module 1 (your webhook trigger). - Module numbers auto-increment based on scenario position. - All field names must match your template's Handlebars variables exactly. ::: Expected webhook payload example: ```json { "invoice_number": "INV-2025-001", "order_date": "2025-01-15", "customer_name": "John Doe", "customer_email": "john@example.com", "line_items": [ { "description": "Website Design", "quantity": 1, "unit_price": "1200.00", "total_amount": "1200.00" }, { "description": "Logo Design Package", "quantity": 2, "unit_price": "250.00", "total_amount": "500.00" } ], "total": "1700.00" } ``` :::tip Other E-commerce Integrations This same approach works with **Shopify**, **Stripe**, **WooCommerce**, **Square**, or any other e-commerce platform in Make. Use their respective trigger modules and map the order data to your template fields. The syntax remains the same: `{{moduleNumber.fieldName}}`. ::: ### Template Scenario Example **Complete scenario:** 1. **Webhook: Custom webhook** – Receives order data from your system. 2. **HTTP: PDFBolt** – Generates invoice PDF. 3. **Email** – Sends PDF to customer. ## Source 2: HTML Convert HTML directly into PDFs within your scenarios – useful for event materials, policy documents, or one-time custom layouts. ### How It Works 1. Add your HTML content in the scenario. 2. Encode HTML to Base64 format. 3. Pass **encoded HTML to PDFBolt**. 4. Receive your PDF. ### Example: Event Program **Real-world scenario:** You're running a conference or webinar and want attendees to download a PDF program containing the full agenda and session information. 1. Add a **Tools – Set variable** module after your trigger: Configure the module: - **Variable name:** `html_content` - **Variable value:** Paste your HTML
**View Complete HTML Example** ```html Tech Summit 2026 Building the Future Together September 18, 2026 | Virtual Event Event Details: Date: September 18, 2026 Time: 9:00 AM - 5:00 PM EST Platform: Zoom (link will be sent via email) Event Schedule 9:00 AM - 10:30 AM Opening Keynote: The Future of AI Speaker: Dr. Sarah Johnson, AI Research Director Exploring emerging trends in artificial intelligence and their impact on business. 10:45 AM - 12:30 PM Workshop: Building Scalable APIs Speaker: Mike Chen, Senior Engineer Hands-on session covering best practices for API design and implementation. Lunch Break (12:30 PM - 1:00 PM) 1:00 PM - 2:45 PM Panel Discussion: Cloud Architecture Panelists: Various industry experts Interactive discussion about modern cloud infrastructure and DevOps practices. 3:00 PM - 5:00 PM Security in Production: Lessons Learned Speaker: Anna Torres, Head of Security Engineering Real-world case studies on securing applications at scale and incident response. Questions? Contact us at events@example.com ```
2. Configure the **HTTP – PDFBolt** module with Base64 encoding: Request content: ```json { "html": "{{base64(2.html_content)}}", "format": "A4", "printBackground": true, "margin": { "top": "30px", "bottom": "30px", "right": "30px", "left": "30px" } } ``` :::warning Base64 Encoding Use Make's built-in `base64()` function to encode HTML. Wrap your HTML variable with `{{base64(your_content)}}` in the request content. ::: ### HTML Scenario Example **Complete scenario:** 1. **Typeform: Watch Responses** – Registration form completed. 2. **Tools: Set Variable** – Store event program HTML layout. 3. **HTTP: PDFBolt** – Convert to PDF (Base64 encoding applied). 4. **Gmail: Send Email** – Deliver program to registered attendee. :::tip PDF Configuration Adjust PDF appearance by modifying parameters in the HTTP module Request content. Explore all options in [Conversion Parameters](/docs/parameters). ::: ## Source 3: URL Transform any public HTTPS webpage into a PDF document – useful for preserving online content, dashboards, or generating reports. ### How It Works 1. Specify the webpage URL. 2. PDFBolt fetches and renders the content. 3. Delivers a PDF of the page. ### Example: Website Change Documentation **Real-world scenario:** Your company has public documentation or a status page that you monitor for updates. When your monitoring tool detects changes, automatically capture the updated page as a PDF for compliance records and team notifications. 1. Configure the **HTTP – PDFBolt** module: Request content: ```json { "url": "{{1.page_url}}", "format": "A4", "landscape": false, "printBackground": true, "waitUntil": "networkidle", "margin": { "top": "20px", "right": "20px", "bottom": "20px", "left": "20px" } } ``` :::tip Dynamic URLs Use `{{1.page_url}}` to capture the URL sent by your monitoring tool's webhook. This allows you to document any page that triggers an update alert. ::: :::info Page Loading `waitUntil: "networkidle"` waits for all network activity to complete before PDF capture – essential for dynamic content and dashboards. ::: ### URL Scenario Example **Complete scenario:** 1. **Webhooks: Custom webhook** – Triggered when page is updated. 2. **HTTP: PDFBolt** – Convert updated page to PDF. 3. **Slack: Create a Message** – Notify team with PDF attachment. 4. **Google Drive: Upload a File** – Archive snapshot for compliance. **Integration options:** Connect this webhook with page monitoring services like Visualping, ChangeTower, or custom scripts that detect website updates. ## Additional Resources ### PDFBolt Documentation - [Template Management Guide](/docs/dashboard/templates) – Detailed creation and management instructions. - [Templates Overview](/docs/pdf-templates) – Template concepts, benefits, and use cases. - [API Endpoints](/docs/api-endpoints) – All available endpoints. - [Conversion Parameters](/docs/parameters) – Complete parameter reference. - [Error Handling](/docs/error-handling) – Error codes and solutions. - [S3 Bucket Upload](/docs/s3-bucket-upload) – Store PDFs permanently. ### Make Resources - Make How-to guides - Make Help Center - Make Community --- ## Zapier Integration Guide Learn how to connect PDFBolt with Zapier for automated PDF creation in your workflows. Build professional documents from templates, HTML content, or web pages through Zapier's no-code automation interface. ## Prerequisites Before starting, ensure you have: 1. **PDFBolt API Key** - [Sign up](https://app.pdfbolt.com/register) or [log in](https://app.pdfbolt.com/login) to your PDFBolt account. - Navigate to the API Credentials section. - Copy your API key for authentication. 2. **Zapier Account** - Active Zapier account (free or paid plan). ## Basic Setup ### Configure Webhooks by Zapier Connect to PDFBolt's API using Zapier's Webhooks action: 1. In your Zap, add an action step and search for **Webhooks by Zapier**. 2. Select **Custom Request** as your action event → Continue. 3. Set up the request parameters: - **Method:** `POST` - **URL:** `https://api.pdfbolt.com/v1/direct` - **Data Pass-Through?** `False` - **Data:** (configuration varies by content source – examples provided below) - **Unflatten:** `No` - **Headers:** - `API-KEY`: `XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX` ### Choose Your Endpoint Select the PDFBolt endpoint based on your workflow needs: | Endpoint | Best For | Returns | |----------|----------|---------| | `/v1/direct` | Immediate PDF delivery | Raw PDF data in response | | `/v1/sync` | URL-based access | JSON with download URL (valid for 24 hours) | | `/v1/async` | Background processing | Webhook callback with results (paid plans only) | :::info Endpoint Details See [API Endpoints](/docs/api-endpoints) for detailed specifications. ::: ### Choose Your Source Select the content source that best fits your use case: | Source | Best For | When to Use | |--------|----------|-------------| | **Templates** | Recurring documents with consistent layouts | Certificates, receipts, invoices – any document you generate repeatedly with different data | | **HTML** | Custom documents | When you need full control over a unique layout | | **URL** | Existing web pages | Archiving documentation, reports, capturing dashboards, saving public web pages | :::info Source Parameters Learn more about source parameters in the [API Documentation](/docs/parameters#source-parameters). ::: ## Source 1: Templates Templates provide the most efficient way to generate consistent, branded PDFs by separating design from data. ### How It Works 1. **Create your template** using one of these options: - Build a custom layout with HTML, CSS, and Handlebars variables in the [Dashboard Template Designer](/docs/dashboard/templates). - Start from a ready-made design in the [template gallery](/pdf-templates). - [Generate a draft with AI](/docs/ai-pdf-template-generation) from a description or reference file. - Create and manage the template programmatically through the [Template API](/docs/api-endpoints/template-api). 2. **Publish for API access** and get your `templateId`. 3. **Send API request** containing just `templateId` and `templateData`. 4. **Receive your PDF** – PDFBolt returns the generated PDF. ### Example: Course Completion Certificate **Real-world scenario:** A student completes your online course. Automatically generate a personalized completion certificate PDF and email it to them. **Step 1:** Set up your trigger (e.g., **Google Forms** or **Teachable** for course completion). **Step 2:** In **Webhooks by Zapier** action, configure the `Data` field: ```json { "templateId": "your-certificate-template-id", "templateData": { "recipient_name": "{{1. Your Full Name}}", "course_title": "{{1. Which course}}", "completion_date": "{{1. Last Submitted Time}}", "instructor_name": "Prof. Lingua Ford", "organization_name": "International Language Center" } } ``` :::info How It Works - `{{1. Field Name}}` references data from step 1 (your trigger). To insert dynamic values, click the **+** icon, search for the field name, and select it. - Step numbers match your Zap sequence position. - Field names must exactly match your template's Handlebars variables. - Combine dynamic fields from triggers with static values as needed. ::: ### Template Zap Example **Complete Zap:** 1. **Google Forms: New Response** – Student submits completion form. 2. **Webhooks by Zapier: Custom Request** – Creates certificate PDF. 3. **Gmail: Send Email** – Delivers certificate to recipient. 4. **Google Drive: Upload File** – Stores certificate copy. :::tip Other Learning Platform Integrations Apply this pattern with **Thinkific**, **Kajabi**, **Teachable**, or similar platforms available in Zapier. Connect completion triggers to your certificate template variables. ::: ## Source 2: HTML Convert HTML directly into PDFs within your Zaps – useful for event materials, announcements, or custom one-time documents. ### How It Works 1. Add your HTML content in a Code step. 2. Encode HTML to Base64 format. 3. Send **encoded HTML to PDFBolt**. 4. Receive your PDF. :::warning Base64 Encoding Required PDFBolt requires Base64-encoded HTML. This means you'll need **Code by Zapier** to encode your HTML before sending it to the Webhooks step. ::: ### Example: Event Program **Real-world scenario:** You're organizing a webinar or conference and want attendees to download a PDF program with schedule, speakers, and session details. **Step 1:** Set up **Typeform: New Entry** trigger for event registration. **Step 2:** Add **Code by Zapier** action with **Run JavaScript**.
**View Complete Code Example** ```javascript // Static event program HTML – same for all attendees const htmlContent = ` Tech Summit 2026 Building the Future Together September 18, 2026 | Virtual Event Event Details: Date: September 18, 2026 Time: 9:00 AM - 5:00 PM EST Platform: Zoom (link will be sent via email) Event Schedule 9:00 AM - 10:30 AM Opening Keynote: The Future of AI Speaker: Dr. Sarah Johnson, AI Research Director Exploring emerging trends in artificial intelligence and their impact on business. 10:45 AM - 12:30 PM Workshop: Building Scalable APIs Speaker: Mike Chen, Senior Engineer Hands-on session covering best practices for API design and implementation. Lunch Break (12:30 PM - 1:00 PM) 1:00 PM - 2:45 PM Panel Discussion: Cloud Architecture Panelists: Various industry experts Interactive discussion about modern cloud infrastructure and DevOps practices. 3:00 PM - 5:00 PM Security in Production: Lessons Learned Speaker: Anna Torres, Head of Security Engineering Real-world case studies on securing applications at scale and incident response. Questions? Contact us at events@example.com `; // Encode to Base64 const html = Buffer.from(htmlContent).toString('base64'); output = { html }; ```
:::info Why Code by Zapier? HTML must be Base64-encoded before sending to PDFBolt. Code by Zapier handles this encoding using `Buffer.from(html).toString('base64')`. ::: **Step 3:** Add **Webhooks by Zapier: Custom Request**. Configure the `Data` field: ```json { "html": "{{2. Html}}", "format": "A4", "printBackground": true, "margin": { "top": "30px", "bottom": "30px", "right": "30px", "left": "30px" } } ``` :::info Dynamic Values - Click the **+** icon in the Data field and select `2. Html` from the Code step output. - This passes the Base64-encoded HTML to PDFBolt. ::: ### HTML Zap Example **Complete Zap:** 1. **Typeform: New Entry** – Registration form submitted. 2. **Code by Zapier: Run JavaScript** – Add and encode HTML. 3. **Webhooks by Zapier: Custom Request** – Generate PDF program. 4. **Gmail: Send Email** – Send confirmation with PDF to attendee. :::tip PDF Customization Adjust PDF formatting by modifying parameters in the Webhooks Data field. See [Conversion Parameters](/docs/parameters) for all available options like page size, orientation, and margins. ::: ## Source 3: URL Convert public HTTPS web pages into PDF documents – useful for saving online materials, dashboard snapshots, or archiving reports. ### How It Works 1. Provide the target webpage URL. 2. PDFBolt retrieves and renders the page. 3. Returns a PDF. ### Example: Daily Dashboard Report **Real-world scenario:** Your team tracks metrics on a web-based analytics dashboard. Every weekday morning, automatically capture the dashboard as PDF and share it on Slack so the team stays informed. **Step 1:** Set up **Schedule by Zapier** trigger. Configure to run **every weekday at 8:00 AM**. **Step 2:** Add **Webhooks by Zapier: Custom Request**. Configure the `Data` field: ```json { "url": "https://analytics.yourcompany.com/dashboard", "format": "A4", "landscape": true, "printBackground": true, "waitUntil": "networkidle", "margin": { "top": "20px", "right": "20px", "bottom": "20px", "left": "20px" } } ``` :::tip Dynamic URLs For pages requiring variable URLs, construct the URL in previous Zap steps. Reference it using `{{Step Number. Field Name}}` in the Data field. ::: :::info Page Loading `waitUntil: "networkidle"` ensures all dashboard charts, data, and dynamic content finish loading before PDF capture – essential for accurate snapshots. ::: ### URL Zap Example **Complete Zap:** 1. **Schedule by Zapier** – Every weekday at 8:00 AM. 2. **Webhooks by Zapier: Custom Request** – Capture dashboard as PDF. 3. **Slack: Send Channel Message** – Share report with team channel. 4. **Google Drive: Upload File** – Archive in Reports folder. ## Additional Resources ### PDFBolt Documentation - [Template Management Guide](/docs/dashboard/templates) – Detailed creation and management instructions. - [Templates Overview](/docs/pdf-templates) – Template concepts, benefits, and use cases. - [API Endpoints](/docs/api-endpoints) – Complete endpoint reference. - [Conversion Parameters](/docs/parameters) – Full parameter documentation. - [Error Handling](/docs/error-handling) – Error codes and solutions. - [S3 Bucket Upload](/docs/s3-bucket-upload) – Store PDFs permanently. ### Zapier Resources - Zapier Guides - Zapier Help Center - Zapier Community --- ## Airtable Integration Guide Learn how to integrate PDFBolt with Airtable to automate PDF generation directly from your bases using Airtable's automation capabilities. ## Prerequisites Before starting, ensure you have: 1. **PDFBolt API Key** - [Sign up](https://app.pdfbolt.com/register) or [log in](https://app.pdfbolt.com/login) to your PDFBolt account. - Navigate to the API Credentials section. - Copy your API key for authentication. 2. **Airtable Account** - Active Airtable workspace. :::note Plan Requirements The **"Run a script"** automation action used in these examples requires an Airtable Team plan or higher. If you're on a Free plan, consider using Zapier or Make as a bridge to PDFBolt (see our [Zapier](/docs/automation-platform-integrations/zapier-integration-guide) or [Make](/docs/automation-platform-integrations/make-integration-guide) guides). ::: ### Choose Your Endpoint Select the PDFBolt endpoint based on your app's needs: | Endpoint | Best For | Returns | |----------|----------|---------| | `/v1/direct` | Immediate PDF delivery | Raw PDF data in response | | `/v1/sync` | URL-based access | JSON with download URL (valid for 24 hours) | | `/v1/async` | Background processing | Webhook callback with results (paid plans only) | :::info Endpoint Details See [API Endpoints](/docs/api-endpoints) for detailed specifications. ::: ### Choose Your Source Select the content source that best fits your use case: | Source | Best For | When to Use | |--------|----------|-------------| | **Templates** | Recurring documents with consistent layouts | Certificates, invoices, receipts – any document you generate repeatedly with different data | | **HTML** | Custom documents | When you need full control over a unique layout | | **URL** | Existing web pages | Archiving documentation, reports, capturing dashboards, saving public web pages | :::info Source Parameters Learn more about source parameters in the [API Documentation](/docs/parameters#source-parameters). ::: ## Source: Templates Templates provide the most efficient way to generate consistent, branded PDFs by separating design from data. ### How It Works 1. **Create your template** using one of these options: - Build a custom layout with HTML, CSS, and Handlebars variables in the [Dashboard Template Designer](/docs/dashboard/templates). - Start from a ready-made design in the [template gallery](/pdf-templates). - [Generate a draft with AI](/docs/ai-pdf-template-generation) from a description or reference file. - Create and manage the template programmatically through the [Template API](/docs/api-endpoints/template-api). 2. **Publish for API access** and get your `templateId`. 3. **Map Airtable fields** to template variables using a script. 4. **Receive your PDF** – PDFBolt merges record data with your template. ## Example: Event Ticket Generation **Real-world scenario:** You're organizing a conference. Instead of manually creating tickets, you need an automated system where someone fills out a registration form and immediately receives their ticket PDF with a QR code via email. ### Step 1: Database Setup In our example, we will use a `Registrations` table with the following sample fields: **Optional: Create an Events table** to store event information: :::info Linked Records Setup The **Event** field in the Registrations table must be a **linked record** field connecting to the Events table. This allows each registration to be associated with a specific event. See Airtable's guide on linked records for setup instructions. ::: ### Step 2: Registration Form In this example, a new record in the table will be created each time the form is submitted: - Turn your table into a public registration form by selecting the fields to display. - Share the form URL on your event website or in marketing emails. - Each submission will automatically create a new record and trigger the automation. ### Step 3: Automation Configuration Navigate to the **Automations** tab in your Airtable base and create a new automation: **Trigger Setup:** - Trigger type: **When record created** - Table: `Registrations` This fires immediately when someone submits the registration form. **Action 1: Run a script (Generate Ticket)** Click **Add action** → Select **Run a script** Before pasting the code, you must configure input variables: 1. Under **"Inputs"**, click **+ Add input variable**. 2. Configure the input: - **Name:** `recordId` - **Value:** Select `Airtable record ID` :::info About Inputs Airtable Inputs let you use values from previous automation triggers and actions. Access them in your script using `input.config()`. This passes the newly created record's ID to the script. ::: **Secure API Key Storage with Secrets:** Instead of hardcoding your API key in the script, use Airtable's **Secrets** feature: 1. Click **+ Add new secret** 2. Name: `PDFBOLT_API_KEY` 3. Value: Paste your actual API key. Then update your script to use the variable: ```javascript const apiKey = input.secret('PDFBOLT_API_KEY') ``` :::tip Secrets Airtable's **Secret** variable type lets you securely store and use sensitive information in scripts without exposing it in code. This is the recommended approach for API keys and other credentials. ::: **Script Implementation:**
**View Code** ```javascript // PDFBolt API configuration const TEMPLATE_ID = 'your-template-id'; let config = input.config(); let recordId = config.recordId; const apiKey = input.secret('PDFBOLT_API_KEY'); let tableRegistrations = base.getTable("Registrations"); let record = await tableRegistrations.selectRecordAsync(recordId); // Get event information from Events table let tableEvents = base.getTable("Events"); let linkedEvent = record.getCellValue('Event'); let eventRecord = await tableEvents.selectRecordAsync(linkedEvent[0].id); // Extract registration data const attendeeData = { attendee_name: record.getCellValueAsString('Full Name'), email: record.getCellValueAsString('Email'), company: record.getCellValueAsString('Company') || 'N/A', ticket_type: record.getCellValueAsString('Ticket Type'), qr_code: record.getCellValueAsString('QR Code'), event_name: eventRecord.getCellValueAsString('Event Name'), event_date: eventRecord.getCellValueAsString('Event Date'), event_time: eventRecord.getCellValueAsString('Event Time'), event_location: eventRecord.getCellValueAsString('Event Location'), }; // Call PDFBolt API try { const response = await fetch('https://api.pdfbolt.com/v1/sync', { method: 'POST', headers: { 'API-KEY': apiKey, 'Content-Type': 'application/json' }, body: JSON.stringify({ templateId: TEMPLATE_ID, templateData: attendeeData }) }); const result = await response.json(); if (response.ok && result.documentUrl) { await tableRegistrations.updateRecordAsync(record.id, { 'Ticket Status': {name: 'Generated'}, 'Ticket PDF': result.documentUrl }); output.set('ticketUrl', result.documentUrl); } else { throw new Error(result.errorMessage || 'Failed to generate ticket'); } } catch (error) { console.error('Error:', error.message); await tableRegistrations.updateRecordAsync(record.id, { 'Ticket Status': {name: 'Failed'} }); throw error; } ```
**Understanding the script:** 1. **Data Collection:** Fetches the newly created attendee record via `input.config()` and extracts field values. 2. **PDF Generation:** Sends data to PDFBolt API which merges it with your template. 3. **Record Update:** Stores the PDF URL back in Airtable. **Action 2: Send email** - Add another action → Select **Gmail: Send email** (or your preferred email service). Configure the step with recipients, subject, and message. - In the message, include the `ticketUrl` generated by the script so the attendee can download it. :::note Using Script Output The `ticketUrl` comes from `output.set('ticketUrl', result.documentUrl)` in the script. Select it from the **"Run a script"** step dropdown when configuring the email body. ::: ### Complete Automations Workflow :::tip Optimization Tips **High volume:** Use `/v1/async` endpoint instead of `/v1/sync` for better performance (paid plans only). **Long-term storage:** Use `customS3PresignedUrl` parameter to store PDFs on your S3 bucket (default URLs expire after 24h). See [S3 Bucket Upload](/docs/s3-bucket-upload). ::: ## Additional Resources ### PDFBolt Documentation - [Template Management Guide](/docs/dashboard/templates) – Create and manage templates. - [Templates Overview](/docs/pdf-templates) – Template concepts, benefits, and use cases. - [API Endpoints](/docs/api-endpoints) – Complete endpoint reference. - [Conversion Parameters](/docs/parameters) – Full parameter documentation. - [Error Handling](/docs/error-handling) – Error codes and solutions. - [S3 Bucket Upload](/docs/s3-bucket-upload) – Store PDFs permanently. ### Airtable Resources - Airtable Guides - Airtable Help Center - Airtable Community --- ## Bubble Integration Guide Learn how to integrate PDFBolt with Bubble.io to automate PDF generation in your no-code applications. Create professional documents from templates, HTML content, or web pages using Bubble's visual workflow builder and API Connector. ## Prerequisites Before starting, ensure you have: 1. **PDFBolt API Key** - [Sign up](https://app.pdfbolt.com/register) or [log in](https://app.pdfbolt.com/login) to your PDFBolt account. - Navigate to the API Credentials section. - Copy your API key for authentication. 2. **Bubble Account** - Active Bubble.io account with an app. - API Connector plugin installed. ## Basic Setup ### Install and Configure API Connector The API Connector plugin enables your Bubble app to communicate with external APIs like PDFBolt. **Step 1: Install API Connector Plugin** 1. In your Bubble editor, go to **Plugins** tab. 2. Click **Add plugins**. 3. Search for **API Connector**. 4. Click **Install** (it's free on all Bubble plans). **Step 2: Add PDFBolt API Connection** 1. Go to **Plugins** → **API Connector**. 2. Click **Add another API**. 3. Configure the API: - **API Name:** `PDFBolt` - **Authentication:** `Private key in header` - **Key name:** `API-KEY` - **Key value:** `XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX` (paste your actual PDFBolt API key) ### Choose Your Endpoint Select the PDFBolt endpoint based on your app's needs: | Endpoint | Best For | Returns | |----------|----------|---------| | `/v1/direct` | Immediate PDF delivery | Raw PDF data in response | | `/v1/sync` | URL-based access | JSON with download URL (valid for 24 hours) | | `/v1/async` | Background processing | Webhook callback with results (paid plans only) | :::info Endpoint Details See [API Endpoints](/docs/api-endpoints) for detailed specifications. ::: ### Choose Your Source Select the content source that best fits your use case: | Source | Best For | When to Use | |--------|----------|-------------| | **Templates** | Recurring documents with consistent layouts | Certificates, invoices, receipts – any document you generate repeatedly with different data | | **HTML** | Custom documents | When you need full control over a unique layout | | **URL** | Existing web pages | Archiving documentation, reports, capturing dashboards, saving public web pages | :::info Source Parameters Learn more about source parameters in the [API Documentation](/docs/parameters#source-parameters). ::: ## Source: Templates Templates provide the most efficient way to generate consistent, branded PDFs by separating design from data – perfect for Bubble apps that need to generate certificates, invoices, or reports. ### How It Works 1. **Create your template** using one of these options: - Build a custom layout with HTML, CSS, and Handlebars variables in the [Dashboard Template Designer](/docs/dashboard/templates). - Start from a ready-made design in the [template gallery](/pdf-templates). - [Generate a draft with AI](/docs/ai-pdf-template-generation) from a description or reference file. - Create and manage the template programmatically through the [Template API](/docs/api-endpoints/template-api). 2. **Publish the template** to make it available via API and get your unique `templateId`. 3. **Call API from Bubble workflow** with `templateId` and data from your database. 4. **Receive your PDF** – Display, download, or email directly from Bubble. ### Example: Student Certification Platform **Real-world scenario:** You're building an online learning platform in Bubble. When students complete a course, they can generate and download a personalized certificate PDF. **1. Data structure for this example:** **Courses** (Data Type) - Name (text) - Instructor (text) - Duration (text) **Enrollments** (Data Type) - Student (User) - Course (Courses) - Completion Date (date) **2. Create API Call** In API Connector, add a new call under your PDFBolt API: - **Name:** `Generate Certificate` - **Use as:** `Action` - **Data type:** `JSON` - **Request type:** `POST` - **URL:** `https://api.pdfbolt.com/v1/sync` **Body:** ```json { "templateId": "your-certificate-template-id", "templateData": { "student_name": "", "course_title": "", "completion_date": "", "instructor_name": "", "course_duration": "" } } ``` :::info Dynamic Parameters The `` syntax in Bubble's API Connector creates dynamic parameters that you'll fill with actual data in your workflows. Bubble automatically detects these and makes them available when you use the API call. ::: **3. Initialize the Call** After configuring the body: - Fill in sample values for each parameter to test: - student_name: `John Doe` - course_title: `Advanced Web Development` - completion_date: `2025-04-10` - instructor_name: `Prof. Sarah Chen` - course_duration: `12 weeks` - Uncheck "Private" checkbox – this allows dynamic values in workflows. - Click **Initialize call** to test the connection. - If successful, Bubble will show the response structure. **4. Build the User Interface** Create a page showing the student's enrolled and completed courses in a Repeating Group with course details and a "Generate Certificate" button. ### Create the Workflow Click on the button "Generate Certificate" → **Start/Edit workflow**. **Step 1: Generate PDF** - Action: **Plugins → PDFBolt – Generate Certificate** - Fill parameters with dynamic data: - `student_name`: `Current User's Name` - `course_title`: `Parent group's Enrollment's Course's name` - `completion_date`: `Parent group's Enrollment's Completion Date:formatted as MMMM D, YYYY` - `instructor_name`: `Parent group's Enrollment's Course's instructor` - `course_duration`: `Parent group's Enrollment's Course's duration` **Step 2: Navigate to PDF** - Action: **Navigation → Open an external website** - Destination: `Result of step 1's documentUrl` - Open in: `New tab` :::tip Result Access Use `Result of step 1` to access API response data in subsequent workflow steps. Bubble makes all returned fields available automatically. ::: ### Template Workflow Example **What happens:** 1. Student completes course (marked in database). 2. Student clicks "Generate Certificate" button. 3. Workflow triggers → generates personalized PDF. 4. PDF opens in new tab for download. :::info Additional PDF Sources Beyond templates, PDFBolt can generate PDFs from: - **[HTML content](/docs/parameters#html)** – Custom document layouts. - **[URLs](/docs/parameters#url)** – Convert any public HTTPS webpage to PDF. ::: ## Additional Resources ### PDFBolt Documentation - [Template Management Guide](/docs/dashboard/templates) – Create and manage templates. - [Templates Overview](/docs/pdf-templates) – Template concepts, benefits, and use cases. - [API Endpoints](/docs/api-endpoints) – Complete endpoint reference. - [Conversion Parameters](/docs/parameters) – Full parameter documentation. - [Error Handling](/docs/error-handling) – Error codes and solutions. - [S3 Bucket Upload](/docs/s3-bucket-upload) – Store PDFs permanently. ### Bubble Resources - Bubble Docs - Bubble Academy - Bubble Forum --- ## Integrately Integration Guide Learn how to integrate PDFBolt with Integrately to automate PDF generation in your workflows. Generate professional documents from templates, HTML content, or web pages using Integrately's automation platform. ## Prerequisites Before starting, ensure you have: 1. **PDFBolt API Key** - [Sign up](https://app.pdfbolt.com/register) or [log in](https://app.pdfbolt.com/login) to your PDFBolt account. - Navigate to the API Credentials section. - Copy your API key for authentication. 2. **Integrately Account** - Active Integrately account (free or paid plan). :::tip Quick Start View PDFBolt on Integrately. ::: ## Basic Setup ### Create Your First Automation 1. Log in to your Integrately account. 2. Click **+ New Automation** in the left sidebar. 3. Choose your trigger app (the app that will start your automation). 4. Search for and select **PDFBolt** as your action app. ### Choose Your Action PDFBolt provides three actions in Integrately: | Action | Best For | Returns | |--------|----------|---------| | **Convert document to PDF in PDFBolt** | Immediate PDF delivery (Direct) | Raw PDF file | | **Convert document to download link in PDFBolt** | URL-based access (Sync) | JSON with download URL (valid for 24 hours) | | **Run asynchronous conversion in PDFBolt** | Background processing (Async) | Request ID for tracking (paid plans only) | :::info Action Details See [API Endpoints](/docs/api-endpoints) for detailed specifications of each conversion method. ::: ### Choose Your Source Before configuring your automation, decide which content source you'll use: | Source | Best For | When to Use | |--------|----------|-------------| | **Templates** | Recurring documents with consistent layouts | Invoices, contracts, certificates – any document you generate repeatedly with different data | | **HTML** | Custom documents | When you need full control over a unique layout | | **URL** | Existing web pages | Archiving documentation, reports, capturing dashboards, saving public web pages | :::info Source Parameters Learn more about source parameters in the [API Documentation](/docs/parameters#source-parameters). ::: ## Source 1: Templates Templates provide the most efficient way to generate consistent, branded PDFs by separating design from data. ### How It Works 1. **Create your template** using one of these options: - Build a custom layout with HTML, CSS, and Handlebars variables in the [Dashboard Template Designer](/docs/dashboard/templates). - Start from a ready-made design in the [template gallery](/pdf-templates). - [Generate a draft with AI](/docs/ai-pdf-template-generation) from a description or reference file. - Create and manage the template programmatically through the [Template API](/docs/api-endpoints/template-api). 2. **Publish the template** to make it available via API and get your unique `templateId`. 3. **Use in Integrately** with just `templateId` and `templateData`. 4. **Receive your PDF** – PDFBolt merges the data with your template and returns the generated PDF. ### Example: Invoice Generation **Real-world scenario:** A customer places an order in your e-commerce system. Automatically generate a professional invoice PDF and email it to the customer. **Step 1:** Create an automation in Integrately and select your trigger app (e.g., Shopify: Order is paid in Shopify, WooCommerce: Order is completed in WooCommerce, etc.). In this example, we use **Google Sheets: Spreadsheet row is created**. **Step 2:** Add **PDFBolt** as your action app. **Step 3:** Select **Convert document to PDF** as the action. **Step 4:** Connect PDFBolt securely by pasting your API key. - Click **Get API Key** – you'll be redirected to PDFBolt's API Credentials section where you can create a new key or copy an existing one. **Step 5:** Select what you want to convert to PDF. In this example, choose **Saved Template** as your source. **Step 6:** Configure the PDF conversion settings. Set your desired options such as **Page Format**, **Margins**, and other parameters. **Required fields:** - **Template ID**: Enter your template ID from PDFBolt. - **Template Data**: Map fields from your trigger to template variables. :::info Dynamic Mapping Use Integrately's field mapping interface to connect trigger data to your template variables. Field names must match your template's Handlebars variables exactly. ::: **Step 7:** Add another action to send the PDF. - Choose **Gmail** or **SendGrid** to send the invoice via email. - Or select **Google Drive** or **Dropbox** to store it in cloud storage. ### Template Automation Example **Complete automation flow:** 1. **Google Sheets: Spreadsheet row is created** – New order data added. 2. **PDFBolt: Convert document to PDF** – Generates invoice PDF. 3. **Gmail: Send email** – Sends invoice to customer. :::tip Other E-commerce Integrations This pattern works with **Shopify**, **WooCommerce**, **Stripe**, **Square**, **BigCommerce**, or any e-commerce platform in Integrately. Map order fields to your template variables. ::: ## Source 2: HTML Convert HTML directly into PDFs – useful for custom documents or one-time layouts. ### How It Works 1. Generate or paste HTML content in your workflow. 2. Integrately automatically handles Base64 encoding. 3. PDFBolt converts **encoded HTML to PDF**. 4. Receive your PDF. :::info Automatic Encoding When you paste HTML into Integrately's PDFBolt action, it **automatically handles Base64 encoding** – no manual encoding step needed. ::: ### Example: Event Program **Real-world scenario:** You're organizing a webinar or conference and want attendees to download a PDF program with schedule, speakers, and session details. **Step 1:** Set up your trigger (e.g., Typeform: Form is submitted in Typeform or Google Forms: Response is created in Google Forms). In this example, we use **Google Forms**. **Step 2:** Add **PDFBolt: Convert document to download link** action (or **Convert document to PDF**). Configure the action: - **Source**: Select **HTML**. - **HTML**: Paste your HTML content (you can include dynamic values from previous steps). - Adjust additional parameters, for example: - **Format**: `A3` - **Print Background**: `true` :::tip PDF Customization Customize PDF formatting by configuring additional parameters in the PDFBolt action. See **[Conversion Parameters](/docs/parameters)** for all available options. In Integrately, you'll also find helpful notes about units, example values, and more. ::: **Step 3:** Add an email action to send the program. - Choose **Gmail** or **SendGrid** to send the event program via email. - Include the PDF download link in the email body or attach the PDF file directly. ### HTML Automation Example **Complete automation:** 1. **Response is created in Google Forms** – Event registration submitted. 2. **PDFBolt: Convert document to PDF** – Generate program PDF from HTML. 3. **Gmail: Send Email** – Deliver program to attendee. ## Source 3: URL Generate PDFs from any publicly accessible HTTPS webpage – useful for archiving web content, capturing dashboards, or saving documentation. ### How It Works 1. Provide the target webpage URL. 2. PDFBolt loads and renders the page. 3. Returns PDF of the rendered page. ### Example: Team Report Distribution **Real-world scenario:** When a team member posts a request in a Slack channel, automatically capture the current state of the dashboard as PDF and distribute it to the team. **Step 1:** Set up **Slack: Message is posted on private channel** trigger. Configure the trigger to monitor a specific channel where team members can request reports (e.g., #reports or #analytics). **Step 2:** Add **PDFBolt: Convert document to download link** action. Configure the action: - **Source**: Select **Webpage URL**. - **URL**: Enter your dashboard or report URL. Adjust additional parameters, for example: - **Format**: `A4` - **Print Background**: `true` - **Margins**: e.g., `20px` **Step 3:** Add **Slack: Send channel message** action. Share the generated report PDF with the team directly in Slack, including the download link. **Step 4:** Add **Google Drive: Upload file** action. Archive the PDF in a designated Google Drive folder (e.g., "Weekly Reports") for historical records and easy access. ### URL Automation Example **Complete automation:** 1. **Slack: Message is posted on private channel** – Team member requests report. 2. **PDFBolt: Convert document to download link** – Capture dashboard as PDF. 3. **Slack: Send channel message** – Share PDF with team. 4. **Google Drive: Upload file** – Archive in Reports folder. ## Converting PDFs with Async Method For background document generation or when working with large files, use the **Run asynchronous conversion** action. ### How It Works 1. Submit conversion request to PDFBolt. 2. Receive immediate `requestId` confirmation. 3. PDFBolt processes in background. 4. Receive webhook notification when complete. :::info Async Benefits Async conversion prevents timeouts, handles larger files, and returns immediately with a request ID – well suited for **batch processing and background workflows**. ::: ### Example: Bulk Rental Agreement Generation **Real-world scenario:** A property management company needs to generate lease agreements for multiple new tenants moving in at the beginning of the month. Use async conversion to generate agreements without waiting for each to complete. **Step 1:** Set up trigger (e.g., **Airtable: Record is created in Airtable**). Configure the trigger to monitor your tenant database. **Step 2:** Add **PDFBolt: Run asynchronous conversion** action. Configure the action: - **Template ID**: Your lease agreement template ID. - **Template Data**: Map tenant and property information from your trigger (e.g., tenant name, property address, lease duration, monthly rent, deposit amount, move-in date, etc.). - **Webhook URL**: Enter your webhook endpoint to receive completion notifications. :::warning Webhook Required Async conversion requires a **Webhook URL** to receive completion notifications. You must provide a valid webhook endpoint. ::: **Step 3:** Store the `requestId` for tracking (optional). You can save the returned `requestId` back to your Airtable for reference and tracking. **Step 4:** Add follow-up actions. For example, update the lease status in your database or send a notification to your property management team. ### Async Automation Example **Complete automation:** 1. **Airtable: Record is created in Airtable** – New tenant lease information added. 2. **PDFBolt: Run asynchronous conversion** – Generate lease agreement PDF. 3. **Airtable: Update record in Airtable** – Store request ID for tracking or update status. ## Additional Resources ### PDFBolt Documentation - [Template Management Guide](/docs/dashboard/templates) – Create and manage templates. - [Templates Overview](/docs/pdf-templates) – Template concepts, benefits, and use cases. - [API Endpoints](/docs/api-endpoints) – Complete endpoint reference. - [Conversion Parameters](/docs/parameters) – Full parameter documentation. - [Error Handling](/docs/error-handling) – Error codes and solutions. ### Integrately Resources - Integrately Help Docs - Integrately Blog - Browse All Available Apps --- ## API Endpoints PDFBolt has three conversion endpoints (Direct, Sync, Async), one usage endpoint for checking your plan and remaining conversions, and Template API endpoints for managing reusable templates. For an interactive view of endpoints, schemas, error responses, and webhook payloads, see the [OpenAPI Reference](/docs/api-reference). ## Base URL All API requests are made to: ```bash https://api.pdfbolt.com ``` ## Authentication ### API Key Use a conversion API key in the `API-KEY` header for `/v1/direct`, `/v1/sync`, `/v1/async`, and `/v1/usage` requests. ```http API-KEY: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX ``` Find your conversion API key on the [API Credentials page](/docs/dashboard/api-keys#conversion-api-keys) in your Dashboard. ### Personal Access Token Protected Template API requests require a Personal Access Token in the `PERSONAL-ACCESS-TOKEN` header. ```http PERSONAL-ACCESS-TOKEN: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX ``` Create a Personal Access Token on the [API Credentials page](/docs/dashboard/api-keys#personal-access-tokens) in your Dashboard. `GET` and `HEAD` requests to `/v1/templates/contract` are public and do not require authentication. ## Quick Example Convert a webpage to PDF and save it as `webpage.pdf`: ```bash curl 'https://api.pdfbolt.com/v1/direct' \ -H 'API-KEY: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' \ -H 'Content-Type: application/json' \ -d '{"url": "https://example.com"}' \ -o webpage.pdf ``` This example converts a URL. You can also use HTML or a template ID with data. See [source parameters](/docs/parameters#source-parameters) for all input options. ## Endpoints Overview | **Endpoint** | **Description** | **Method** | **Response** | **Documentation** | |--------------|---------------------------------------------------------------------------------------|------------|----------------------------------------------------------|---------------------------------------------------------------------------------------------------------------| | `/v1/direct` | Generates PDFs from URLs, HTML, or templates and returns them in the response. | `POST` | Raw PDF or Base64-encoded PDF in the HTTP response body. | [Direct Parameters](/docs/api-endpoints/direct#body-parameters) [Common Parameters](/docs/parameters/) | | `/v1/sync` | Generates PDFs from URLs, HTML, or templates and provides a downloadable URL. | `POST` | JSON containing a downloadable URL to the generated PDF. | [Sync Parameters](/docs/api-endpoints/sync#body-parameters) [Common Parameters](/docs/parameters/) | | `/v1/async` | Processes URLs, HTML, or templates and sends results via webhook. | `POST` | JSON with `requestId`, plus webhook callback when generation completes. | [Async Parameters](/docs/api-endpoints/async#body-parameters) [Common Parameters](/docs/parameters/) | | `/v1/usage` | Returns the current plan, recurring and one-time conversion packages, and remaining conversions. | `GET` | JSON with `plan`, `recurring`, and `oneTime` conversion details. | [Usage Response](/docs/api-endpoints/usage-monitoring#response-parameters) | | `/v1/templates/*` | Lists, validates, previews, compares, saves, and publishes reusable templates. | `GET` / `POST` | JSON or raw PDF, depending on operation. | [Template API](/docs/api-endpoints/template-api) | ## Response Headers PDFBolt returns headers that help you track conversion cost, monitor rate limits, and verify async webhooks. When a Conversion API, Template API preview, or Template API diff request passes the conversion rate limiter, its response includes the rate-limit headers below. `/v1/usage` and successful Template API management responses do not include them. Template API management responses with HTTP `429` include Retry-After. Header Applies to Description x-pdfbolt-conversion-cost /v1/direct, /v1/sync, Template API preview/diff, and async webhook callbacks Document credits charged for the request. Content-Disposition Successful /v1/direct responses Controls inline vs attachment delivery. Includes filename when provided. x-pdfbolt-signature Async webhook callbacks HMAC-SHA256 signature of the raw request body, prefixed sha256=. x-pdfbolt-limit-minute Conversion API and Template API preview/diff Request limit for the current rolling minute. x-pdfbolt-remaining-minute Conversion API and Template API preview/diff Requests remaining in the current rolling minute. x-pdfbolt-limit-hour Conversion API and Template API preview/diff Request limit for the current rolling hour. x-pdfbolt-remaining-hour Conversion API and Template API preview/diff Requests remaining in the current rolling hour. x-pdfbolt-limit-day Conversion API and Template API preview/diff Request limit for the current rolling 24‑hour window. x-pdfbolt-remaining-day Conversion API and Template API preview/diff Requests remaining in the current rolling 24‑hour window. Retry-After Template API management responses with HTTP 429 Number of seconds to wait before retrying the request. See [Rate Limits](/docs/rate-limits) for request limits and concurrency rules. ## Endpoint Documentation Read the detailed documentation for each endpoint: ## Related Topics - [Error Handling](/docs/error-handling) – HTTP status codes, error response format, and recommended actions. - [Rate Limits](/docs/rate-limits) – Per-plan request limits, concurrency rules, and 429 handling. - [IP Addresses](/docs/ip-addresses) – Static outbound IPs to add to your firewall allowlist. - [Conversion Parameters](/docs/parameters) – Full reference for all PDF generation options. --- ## Direct Conversion The `/v1/direct` endpoint generates a PDF from a URL, HTML, or template ID and returns it directly in the response body. Use it when you need the PDF immediately: save to disk, attach to an email, or show an in-app preview while your user is waiting. ## Endpoint Details **Method**: `POST` ```bash https://api.pdfbolt.com/v1/direct ``` ## Success Example Example request body for converting a URL to PDF: ```json { "url": "https://example.com" } ``` Complete request with authentication, saving the result as webpage.pdf: ```bash curl 'https://api.pdfbolt.com/v1/direct' \ -H 'API-KEY: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' \ -H 'Content-Type: application/json' \ -d '{"url": "https://example.com"}' \ -o webpage.pdf ``` To save response headers (rate limits, conversion cost, content disposition) alongside the PDF, add -D headers.txt: ```bash curl -D headers.txt 'https://api.pdfbolt.com/v1/direct' \ -H 'API-KEY: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' \ -H 'Content-Type: application/json' \ -d '{"url": "https://example.com"}' \ -o webpage.pdf ``` The response contains raw PDF bytes, or Base64-encoded PDF text when [`isEncoded`](/docs/api-endpoints/direct#isencoded) is `true`. ## Failure Example API-level failures return a JSON error object. Infrastructure-generated responses, such as `503` and `504`, may use a different format. See [Error Handling](/docs/error-handling) for shared error codes and recommended actions, and the [OpenAPI Reference](/docs/api-reference) for the exact responses supported by this endpoint. ```json { "url": "https://example.com", "waitForFunction": "() => document.body.innerText.includes('Ready to Download')" } ``` ```json { "timestamp": "2026-04-30T10:28:43Z", "httpErrorCode": 408, "errorCode": "CONVERSION_TIMEOUT", "errorMessage": "Conversion process timed out. Please see https://pdfbolt.com/docs/parameters#timeout, https://pdfbolt.com/docs/parameters#waituntil and https://pdfbolt.com/docs/parameters#waitforfunction parameters." } ``` ## Body Parameters :::info Common Parameters Below is the only parameter specific to `/v1/direct`. For parameters shared across the Conversion API endpoints, see [Conversion Parameters](/docs/parameters). ::: ### isEncoded **Type:** `boolean` **Required:** No **Default Value:** `false` **Details:** The `isEncoded` parameter determines the format of the PDF data returned in the response: - When `true`: returns Base64-encoded PDF with `Content-Type: text/plain`. - When `false` (default): returns raw PDF binary with `Content-Type: application/pdf`. **Usage:** ```json { "url": "https://example.com", "isEncoded": true } ``` :::warning Base64 Size Overhead Base64-encoded responses are approximately 33% larger than raw binary, increasing transfer time and storage usage. ::: ## Response The response format depends on the `isEncoded` parameter: | Value | Response Format | Content Type | |---------|--------------------|-------------------| | `true` | Base64-encoded PDF | `text/plain` | | `false` | Raw PDF binary | `application/pdf` | For response headers, see [API Response Headers](/docs/api-endpoints#response-headers). ## Next Steps --- ## Sync Conversion The `/v1/sync` endpoint generates a PDF from a URL, HTML, or a published template ID with JSON data and returns a JSON response with a temporary download URL. By default, files stored in PDFBolt's temporary storage expire after 24 hours. Use it when your application needs a download URL instead of PDF bytes. To store the PDF in your own bucket instead, provide `customS3PresignedUrl`. ## Endpoint Details **Method**: `POST` ```bash https://api.pdfbolt.com/v1/sync ``` ## Success Example Example request body for converting a URL to PDF: ```json { "url": "https://example.com" } ``` Complete request with authentication: ```bash curl 'https://api.pdfbolt.com/v1/sync' \ -H 'API-KEY: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' \ -H 'Content-Type: application/json' \ -d '{"url": "https://example.com"}' ``` To inspect the rate-limit and conversion-cost response headers, add -D -: ```bash curl -D - 'https://api.pdfbolt.com/v1/sync' \ -H 'API-KEY: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' \ -H 'Content-Type: application/json' \ -d '{"url": "https://example.com"}' ``` ```json { "requestId": "db347fe5-7b72-45f6-92f9-a7b7755ab6c8", "status": "SUCCESS", "errorCode": null, "errorMessage": null, "documentUrl": "https://s3.pdfbolt.com/pdfbolt_ec2950a1-f835-4be6-bab2-69490b53b1f9_2026-04-30T10-44-09Z.pdf", "expiresAt": "2026-05-01T10:44:09Z", "isAsync": false, "duration": 606, "documentSizeMb": 0.02, "isCustomS3Bucket": false } ``` ## Failure Example API-level failures return a JSON error object. Infrastructure-generated responses, such as `503` and `504`, may use a different format. See [Error Handling](/docs/error-handling) for shared error codes and recommended actions, and the [OpenAPI Reference](/docs/api-reference) for the exact responses supported by this endpoint. ```json { "url": "https://example.com", "waitForFunction": "() => document.body.innerText.includes('Ready to Download')" } ``` ```json { "timestamp": "2026-04-30T10:25:07Z", "httpErrorCode": 408, "errorCode": "CONVERSION_TIMEOUT", "errorMessage": "Conversion process timed out. Please see https://pdfbolt.com/docs/parameters#timeout, https://pdfbolt.com/docs/parameters#waituntil and https://pdfbolt.com/docs/parameters#waitforfunction parameters." } ``` ## Body Parameters :::info Common Parameters The parameter below is available on the `/v1/sync` and `/v1/async` endpoints. For common Conversion API parameters, see [Conversion Parameters](/docs/parameters). ::: ### customS3PresignedUrl **Type:** `string` **Required:** No **Details:** Specifies an HTTPS pre-signed PUT URL for direct upload to your S3-compatible bucket. The URL must be no longer than 2048 characters. When provided, `documentUrl` and `expiresAt` are `null`, and `isCustomS3Bucket` is `true`. If not provided, the document is stored in PDFBolt's temporary storage for 24 hours. See [Uploading to Your S3 Bucket](/docs/s3-bucket-upload) for setup details. **Usage:** ```json { "url": "https://example.com", "customS3PresignedUrl": "https://your-bucket.s3.amazonaws.com/document.pdf?" } ``` :::info Plan requirement `customS3PresignedUrl` is available on paid plans. Free plan users should omit this parameter and use PDFBolt's default temporary storage instead. ::: ## Response Parameters Response fields for successful requests. For failure responses, see [Error Response Format](/docs/error-handling#error-response-format). | **Parameter** | **Type** | **Description** | **Possible Values** | **Example Value** | |--------------------|-------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------|------------------------------------------------------------------------------------------------| | `requestId` | `string` (UUID) | Unique identifier for the request. | Any valid UUID | `db347fe5-7b72-45f6-92f9-a7b7755ab6c8` | | `status` | `string` (Enum) | Status of the request. | `SUCCESS` | `SUCCESS` | | `errorCode` | `null` | Always `null` for successful requests. | `null` | `null` | | `errorMessage` | `null` | Always `null` for successful requests. | `null` | `null` | | `documentUrl` | `string` (URL) or `null` | URL to the generated document.This will be `null` if `customS3PresignedUrl` is used, as the PDF is uploaded directly to the provided S3 bucket. | Any valid URL or `null` | `https://s3.pdfbolt.com/pdfbolt_ec2950a1-f835-4be6-bab2-69490b53b1f9_2026-04-30T10-44-09Z.pdf` | | `expiresAt` | `string` (ISO 8601) or `null` | Expiration date and time of the document stored in PDFBolt's temporary storage.Will be `null` if `customS3PresignedUrl` is provided. | ISO 8601 datetime string in UTC or `null` | `2026-05-01T10:44:09Z` | | `isAsync` | `boolean` | Indicates if the operation is asynchronous.Will always be `false` for the `/v1/sync` endpoint. | `false` | `false` | | `duration` | `integer` | PDF conversion time in milliseconds. | Any non-negative integer | `606` | | `documentSizeMb` | `number` | Size of the document in megabytes. | Any non-negative number | `0.02` | | `isCustomS3Bucket` | `boolean` | Indicates if the generated document was uploaded to a user-provided custom S3 bucket or PDFBolt's default storage. | `true` `false` | `false` | For response headers, see [API Response Headers](/docs/api-endpoints#response-headers). ## Next Steps --- ## Async Conversion The `/v1/async` endpoint accepts a PDF conversion request from a URL, HTML, or a published template with JSON data and immediately returns a `requestId`. PDFBolt processes the request in the background and sends the final result to your HMAC-signed webhook when the conversion succeeds or all conversion attempts fail. The `webhook` parameter is required. :::info Plan requirement The **`/v1/async`** endpoint is available on paid plans. Free plan users can use [`/v1/direct`](/docs/api-endpoints/direct) and [`/v1/sync`](/docs/api-endpoints/sync). ::: ## Endpoint Details **Method**: `POST` ```bash https://api.pdfbolt.com/v1/async ``` ## Success Example Example request body with the required webhook URL: ```json { "url": "https://example.com", "webhook": "https://your-app.com/webhook" } ``` Complete request with authentication: ```bash curl 'https://api.pdfbolt.com/v1/async' \ -H 'API-KEY: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' \ -H 'Content-Type: application/json' \ -d '{"url": "https://example.com", "webhook": "https://your-app.com/webhook"}' ``` ```json { "requestId": "4da0a428-16e0-4c95-b1d3-a8f475ed717e" } ``` PDFBolt sends a POST request to your webhook URL. **Headers**: ```http Content-Type: application/json x-pdfbolt-signature: sha256=a1b2c3d4e5f6... x-pdfbolt-conversion-cost: 1 ``` **Body**: ```json { "requestId": "4da0a428-16e0-4c95-b1d3-a8f475ed717e", "status": "SUCCESS", "errorCode": null, "errorMessage": null, "documentUrl": "https://s3.pdfbolt.com/pdfbolt_89878444-79a5-4115-beeb-f36745d61cf7_2026-04-30T10-47-03Z.pdf", "expiresAt": "2026-05-01T10:47:03Z", "isAsync": true, "duration": 574, "documentSizeMb": 0.02, "isCustomS3Bucket": false } ``` ## Failure Example Requests rejected before acceptance return an HTTP error and do not trigger a webhook. After a request is accepted, a final conversion failure, such as a timeout or target page error, is delivered to the webhook with status: "FAILURE" and an `errorCode`. See [Async Conversion Failures](/docs/error-handling#async-conversion-failures) for recommended actions and the [OpenAPI Reference](/docs/api-reference) for the exact schema. ```json { "url": "https://example.com", "webhook": "https://your-app.com/webhook", "waitForFunction": "() => document.body.innerText.includes('Ready to Download')" } ``` ```json { "requestId": "a3bf2ab7-5ef3-4d8b-a715-765697611dce" } ``` PDFBolt sends a POST request to your webhook URL. **Headers**: ```http Content-Type: application/json x-pdfbolt-signature: sha256=f7e8d9c0b1a2... x-pdfbolt-conversion-cost: 0 ``` **Body**: ```json { "requestId": "a3bf2ab7-5ef3-4d8b-a715-765697611dce", "status": "FAILURE", "errorCode": "CONVERSION_TIMEOUT", "errorMessage": "Conversion process timed out. Please see https://pdfbolt.com/docs/parameters#timeout, https://pdfbolt.com/docs/parameters#waituntil and https://pdfbolt.com/docs/parameters#waitforfunction parameters.", "documentUrl": null, "expiresAt": null, "isAsync": true, "duration": 30541, "documentSizeMb": null, "isCustomS3Bucket": false } ``` ## Body Parameters :::info Common Parameters The parameters below configure `/v1/async` processing and webhook delivery. `customS3PresignedUrl` is also available on the `/v1/sync` endpoint. For parameters shared by all Conversion API endpoints, see [Conversion Parameters](/docs/parameters). ::: ### webhook **Type:** `string` **Required:** Yes **Details:** The HTTPS URL to which PDFBolt sends the conversion result. It must accept POST requests and be publicly reachable. **Validation rules:** - HTTPS only (HTTP is rejected). - Maximum length: 2048 characters. - Test domains (e.g., `.test`) are not accepted. **Usage:** ```json { "url": "https://example.com", "webhook": "https://your-app.com/endpoint" } ``` ### customS3PresignedUrl **Type:** `string` **Required:** No **Details:** Specifies an HTTPS pre-signed PUT URL for direct upload to your S3-compatible bucket. The URL must be no longer than 2048 characters. When provided, the webhook reports `documentUrl` and `expiresAt` as `null`, and `isCustomS3Bucket` as `true`. If not provided, the document is stored in PDFBolt's temporary storage for 24 hours. See [Uploading to Your S3 Bucket](/docs/s3-bucket-upload) for setup details. **Usage:** ```json { "url": "https://example.com", "webhook": "https://your-app.com/endpoint", "customS3PresignedUrl": "https://your-bucket.s3.amazonaws.com/document.pdf?" } ``` ### additionalWebhookHeaders **Type:** `object` **Required:** No **Details:** Adds custom headers to webhook callbacks. Use them to pass context to your webhook endpoint. **Validation rules:** - JSON object with string keys and string values. - Header values cannot be `null`. - Maximum 10 headers. - The combined UTF-8 size of all header names and values must not exceed 4 KB. :::warning Reserved headers Set automatically by PDFBolt – do not include: `Content-Type`, `x-pdfbolt-signature`, `x-pdfbolt-conversion-cost`. ::: **Usage:** ```json { "url": "https://example.com", "webhook": "https://your-app.com/endpoint", "additionalWebhookHeaders": { "X-Custom-Header-1": "Value1", "X-Custom-Header-2": "Value2" } } ``` :::tip Common Use Cases Use `additionalWebhookHeaders` to: - Add tenant or environment identifiers (e.g., `X-Tenant-Id`, `X-Environment`). - Forward user or session context (e.g., `X-User-Id`). - Include correlation IDs for request tracing across services. ::: ### retryDelays **Type:** `Array` **Required:** No **Details:** The `retryDelays` parameter defines a custom retry schedule for failed conversions. Each element is the **delay in minutes** before the next retry attempt, measured from the last failed attempt. Total attempts = 1 (initial) + length of the `retryDelays` array. **Validation rules:** - Array of positive integers in minutes. Floating-point values are rejected. - Minimum 1 element, maximum 5 elements (empty array is rejected). - Values must be in strictly ascending order (e.g., `[5, 5, 10]` is rejected). - Maximum value per element: 1440 (24 hours). :::info Retry Behavior - PDFBolt attempts the webhook only after a conversion succeeds or all retries fail. Intermediate retry failures do not trigger a callback. - Only successful conversions consume credits, regardless of how many retries occur. ::: **Usage:** ```json { "url": "https://example.com", "webhook": "https://your-app.com/endpoint", "retryDelays": [1, 5, 15] } ``` In the example above, there are 4 total attempts (1 initial + 3 retries). If the first conversion attempt fails: - Retry 1: 1 minute after the first failure. - Retry 2: 5 minutes after retry 1 fails. - Retry 3: 15 minutes after retry 2 fails. - If any attempt succeeds, the webhook is called immediately with `SUCCESS` status and remaining retries are skipped. - If all retries fail, the webhook is called with `FAILURE` status. ## Response Parameters | **Parameter** | **Type** | **Description** | **Possible Values** | **Example Value** | |---------------|-----------------|----------------------------------------------------------------|---------------------|----------------------------------------| | `requestId` | `string` (UUID) | Unique identifier for the request. | Any valid UUID | `4da0a428-16e0-4c95-b1d3-a8f475ed717e` | ## Webhook Request Parameters | **Parameter** | **Type** | **Description** | **Possible Values** | **Example Value** | |--------------------|-------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `requestId` | `string` (UUID) | Unique identifier for the request. | Any valid UUID | `4da0a428-16e0-4c95-b1d3-a8f475ed717e` | | `status` | `string` (Enum) | Status of the request. | `SUCCESS` `FAILURE` | `SUCCESS` | | `errorCode` | `string` or `null` | Error code if the request failed.Will be `null` if status is `SUCCESS`. | Refer to `errorCode` values in the [HTTP Status Codes](/docs/error-handling#http-status-codes) table for all possible values | `CONVERSION_TIMEOUT` | | `errorMessage` | `string` or `null` | Error message if the request failed.Will be `null` if status is `SUCCESS`. | Any string or `null` | `Conversion process timed out. Please see https://pdfbolt.com/docs/parameters#timeout, https://pdfbolt.com/docs/parameters#waituntil and https://pdfbolt.com/docs/parameters#waitforfunction parameters.` | | `documentUrl` | `string` (URL) or `null` | URL to the generated document.Will be `null` if `customS3PresignedUrl` is provided.Will be `null` if status is `FAILURE`. | Any valid URL or `null` | `https://s3.pdfbolt.com/pdfbolt_89878444-79a5-4115-beeb-f36745d61cf7_2026-04-30T10-47-03Z.pdf` | | `expiresAt` | `string` (ISO 8601) or `null` | Expiration date and time of the document.Will be `null` if `customS3PresignedUrl` is provided.Will be `null` if status is `FAILURE`. | ISO 8601 datetime string in UTC or `null` | `2026-05-01T10:47:03Z` | | `isAsync` | `boolean` | Indicates if the operation is asynchronous.Will always be `true` for the `/v1/async` endpoint. | `true` | `true` | | `duration` | `integer` | PDF conversion time in milliseconds. | Any non-negative integer | `574` | | `documentSizeMb` | `number` or `null` | Size of the document in megabytes.Will be `null` if status is `FAILURE`. | Any non-negative number or `null` | `0.02` | | `isCustomS3Bucket` | `boolean` | Whether the async conversion request used a user-provided S3-compatible bucket. | `true` `false` | `false` | ## Webhook Delivery Behavior PDFBolt attempts one webhook callback per accepted conversion – after the conversion succeeds or all retries fail. `retryDelays` retries the **conversion attempt itself** – it does not retry **webhook delivery**. If webhook delivery fails because of a timeout, a `5xx` response, or a network error, PDFBolt does not retry it automatically. To investigate: - Check the conversion status in your Dashboard using the `requestId`. - Review the **Webhook Result** column in the Dashboard – it shows the HTTP response code returned by your endpoint. **Unreachable** means the delivery failed (timeout or network error). **Recommended client-side practices:** - After verifying the signature, return a fast `2xx` response and run any slow follow-up work in your own background job. - Make your handler idempotent – use `requestId` as a deduplication key. - Monitor your endpoint uptime; if it goes down, expect missed deliveries during the outage. For response and webhook headers, see [API Response Headers](/docs/api-endpoints#response-headers). ## Webhook Signature Verification Each webhook request includes an `x-pdfbolt-signature` header, so you can confirm it genuinely came from PDFBolt. The signature is an **HMAC-SHA256** hash formatted as: ```http x-pdfbolt-signature: sha256= ``` ### How It Works 1. PDFBolt computes `HMAC-SHA256(webhook_signature_key, raw_request_body)` using your webhook signature key. 2. The result is hex-encoded and prefixed with `sha256=`. 3. The signature is sent in the `x-pdfbolt-signature` header of every webhook request (both success and failure). ### Finding Your Webhook Signature Key Find your webhook signature key in the **Webhook Signature** section of the [API Credentials](https://app.pdfbolt.com/api-keys) page in your Dashboard. ### Verification Examples :::tip Signature Verification - Always compute the HMAC from the **raw request body** – before any JSON parsing. If you parse and re-serialize the JSON, key ordering or whitespace may change, producing a different hash. - Use a **constant-time comparison** function to prevent timing attacks. ::: ```js const crypto = require('crypto'); function verifyWebhookSignature(rawBody, signatureHeader, webhookSignatureKey) { const expected = 'sha256=' + crypto .createHmac('sha256', webhookSignatureKey) .update(rawBody, 'utf8') .digest('hex'); const expectedBuf = Buffer.from(expected); const receivedBuf = Buffer.from(signatureHeader); if (expectedBuf.length !== receivedBuf.length) { return false; } return crypto.timingSafeEqual(expectedBuf, receivedBuf); } ``` ```python def verify_webhook_signature(raw_body: bytes, signature_header: str, webhook_signature_key: str) -> bool: expected = 'sha256=' + hmac.new( webhook_signature_key.encode('utf-8'), raw_body, hashlib.sha256 ).hexdigest() return hmac.compare_digest(expected, signature_header) ``` ```java public static boolean verifyWebhookSignature(byte[] rawBody, String signatureHeader, String webhookSignatureKey) throws Exception { Mac mac = Mac.getInstance("HmacSHA256"); mac.init(new SecretKeySpec(webhookSignatureKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256")); byte[] hash = mac.doFinal(rawBody); StringBuilder hex = new StringBuilder(); for (byte b : hash) { hex.append(String.format("%02x", b)); } String expected = "sha256=" + hex; return MessageDigest.isEqual(expected.getBytes(StandardCharsets.UTF_8), signatureHeader.getBytes(StandardCharsets.UTF_8)); } ``` ```php function verifyWebhookSignature(string $rawBody, string $signatureHeader, string $webhookSignatureKey): bool { $expected = 'sha256=' . hash_hmac('sha256', $rawBody, $webhookSignatureKey); return hash_equals($expected, $signatureHeader); } ``` ```csharp using System.Security.Cryptography; using System.Text; static bool VerifyWebhookSignature(byte[] rawBody, string signatureHeader, string webhookSignatureKey) { using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(webhookSignatureKey)); byte[] hash = hmac.ComputeHash(rawBody); string expected = "sha256=" + Convert.ToHexString(hash).ToLowerInvariant(); return CryptographicOperations.FixedTimeEquals( Encoding.UTF8.GetBytes(expected), Encoding.UTF8.GetBytes(signatureHeader) ); } ``` ```go "crypto/hmac" "crypto/sha256" "crypto/subtle" "encoding/hex" ) func verifyWebhookSignature(rawBody []byte, signatureHeader, webhookSignatureKey string) bool { mac := hmac.New(sha256.New, []byte(webhookSignatureKey)) mac.Write(rawBody) expected := "sha256=" + hex.EncodeToString(mac.Sum(nil)) return subtle.ConstantTimeCompare([]byte(expected), []byte(signatureHeader)) == 1 } ``` ```rust // Cargo.toml dependencies: hmac = "0.12", sha2 = "0.10", hex = "0.4" use hmac::{Hmac, Mac}; use sha2::Sha256; type HmacSha256 = Hmac; fn verify_webhook_signature(raw_body: &[u8], signature_header: &str, webhook_signature_key: &str) -> bool { let hex_part = match signature_header.strip_prefix("sha256=") { Some(hex) => hex, None => return false, }; let received_bytes = match hex::decode(hex_part) { Ok(bytes) => bytes, Err(_) => return false, }; let mut mac = HmacSha256::new_from_slice(webhook_signature_key.as_bytes()) .expect("HMAC can take key of any size"); mac.update(raw_body); mac.verify_slice(&received_bytes).is_ok() } ``` :::note Framework Setup Each framework reads request bodies differently. Here's how to access raw bytes for HMAC verification: - **Express**: register express.raw({ type: 'application/json' }) middleware on the webhook route specifically (route-specific middleware avoids conflicts with global `express.json()`). - **Flask**: use request.get_data() (returns raw bytes) instead of `request.json`. - **Spring**: declare your handler parameter as @RequestBody byte[] rawBody (resolved via `ByteArrayHttpMessageConverter`). - **ASP.NET Core**: read `Request.Body` stream into `byte[]` – use PipeReader (recommended) or `MemoryStream.CopyToAsync` for the simpler approach. - **PHP / Laravel**: read raw input with file_get_contents('php://input') (or `$request->getContent()` in Laravel, inherited from Symfony HttpFoundation). Go (Gin, Echo) and Rust (Axum, Actix-web) read raw body by default – no setup needed. ::: ## Next Steps --- ## Usage Monitoring The `/v1/usage` endpoint returns your current plan, remaining conversions, overage, and expiration dates. It uses the same `API-KEY` as the Conversion API endpoints. Use it to track conversion usage, not API request limits. ## Endpoint Details **Method**: `GET` ```bash https://api.pdfbolt.com/v1/usage ``` ## Success Example Complete request with authentication: ```bash curl 'https://api.pdfbolt.com/v1/usage' \ -H 'API-KEY: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' ``` ```json { "plan": "BASIC_MONTHLY", "recurring": [ { "total": 2000, "left": 1322, "expires": "2026-05-22T23:59:59Z", "overage": 0 } ], "oneTime": [] } ``` ## Failure Example If the request fails (e.g., missing or invalid `API-KEY`), the response is a JSON error object. See [Error Handling](/docs/error-handling). ```bash curl 'https://api.pdfbolt.com/v1/usage' ``` ```json { "timestamp": "2026-04-30T17:27:52Z", "httpErrorCode": 401, "errorCode": "UNAUTHORIZED", "errorMessage": "The API key is missing, invalid or has been blocked. Please verify your key or contact support." } ``` ## Response Parameters | **Parameter** | **Type** | **Description** | **Possible Values** | **Example Value** | |---------------------|---------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------| | `plan` | `string` (Enum) | Subscription plan name. | `FREE` `BASIC_MONTHLY` `GROWTH_MONTHLY` `ENTERPRISE_1_MONTHLY` `ENTERPRISE_PRO_MONTHLY` | `BASIC_MONTHLY` | | `recurring` | `array` | Recurring conversion details for the current billing period. | Array of objects | `[{"total": 2000, "left": 1322, "expires": "2026-05-22T23:59:59Z", "overage": 0}]` | | `recurring.total` | `integer` | Total number of recurring conversions available. | Any positive integer | `2000` | | `recurring.left` | `integer` | Number of remaining recurring conversions.Includes 0 if no conversions are left. | Any non-negative integer | `1322` | | `recurring.expires` | `string` (ISO 8601) | Expiration date and time for the current recurring period. | ISO 8601 datetime string in UTC | `2026-05-22T23:59:59Z` | | `recurring.overage` | `integer` | Number of conversions used beyond your plan limit and tracked as overage.Overage is disabled by default and can be enabled in your Dashboard. | Any non-negative integer | `0` | | `oneTime` | `array` | Active one-time conversion packages with remaining conversions. | Array of objects | `[{"total": 500, "left": 350, "expires": "2026-09-15T23:59:59Z"}]` | | `oneTime.total` | `integer` | Total number of one-time conversions in the package. | Any positive integer | `500` | | `oneTime.left` | `integer` | Number of remaining one-time conversions. | Any positive integer | `350` | | `oneTime.expires` | `string` (ISO 8601) | Expiration date and time for the one-time credit package. | ISO 8601 datetime string in UTC | `2026-09-15T23:59:59Z` | ## Next Steps --- ## Template API The Template API provides REST endpoints for managing reusable PDF templates from internal tools, CI workflows, or AI coding agents without opening the Dashboard Template Designer. ## Base URL Send Template API requests to: ```bash https://api.pdfbolt.com/v1/templates ``` ## Authentication Except for public `GET` and `HEAD` requests to `/v1/templates/contract`, Template API requests require a Personal Access Token. Create one in the **Personal Access Tokens** section of the [API Credentials](https://app.pdfbolt.com/api-keys) page in the Dashboard. Send the token in the `PERSONAL-ACCESS-TOKEN` header with every protected request: ```http PERSONAL-ACCESS-TOKEN: ``` Store tokens in an environment variable or secret manager. Never expose them in client-side code, logs, or source control. Each Personal Access Token belongs to the user who created it, not to a team. Requests authenticate as that user and can access every template available to the user in the Dashboard. In a shared team, this may include templates created by other team members. Personal Access Tokens cannot be restricted to specific templates or operations and do not expire automatically. You can have up to five tokens, including deactivated tokens. Deactivation takes effect immediately and cannot be undone in the Dashboard. ## How the Template API Works Use this workflow to create and publish a template, then generate PDFs through the Conversion API: 1. **Retrieve the contract.** Call `GET /v1/templates/contract` to get the current template rules and supported fields. 2. **Prepare the template.** Create a complete HTML document with any Handlebars placeholders and Base64-encode it. 3. **Validate the template.** Check the payload and template syntax without rendering a PDF. 4. **Preview and review.** Render the template as a PDF and visually inspect every page. Revise and repeat until it is ready. 5. **Save the draft.** Store the reviewed template without changing the published version used by the Conversion API. 6. **Verify the draft.** Retrieve the template and confirm that the intended values were saved. 7. **Publish the draft.** Make it the version used by the Conversion API. 8. **Generate PDFs.** Send the `templateId` and document-specific `templateData` to the Conversion API. ## Endpoints Overview The Template API provides the following endpoints: | Method | Endpoint | Purpose | | ------ | ------------------------------------ | ---------------------------------------------------------------------------------- | | `GET` | `/v1/templates/contract` | Retrieve the versioned Template Contract. | | `HEAD` | `/v1/templates/contract` | Check the public Template Contract endpoint without returning a response body. | | `GET` | `/v1/templates` | List the current team's templates. | | `GET` | `/v1/templates/{templateId}` | Retrieve template details, including content, sample data, and PDF parameters. | | `POST` | `/v1/templates/validate` | Validate a template payload without rendering a PDF. | | `POST` | `/v1/templates/preview` | Render a template preview and return a temporary URL by default. | | `POST` | `/v1/templates/{templateId}/diff` | Compare proposed template changes with the published version. | | `POST` | `/v1/templates/drafts` | Create or update a template draft. | | `POST` | `/v1/templates/{templateId}/publish` | Publish the active draft. | ## Request Fields Template API request bodies use the fields below. Each endpoint accepts a different subset. See the endpoint reference for the exact request shape. | Field | Type | Description | |--------------------------------------|-----------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `templateId` | `string` (UUID) | Existing template UUID used for draft updates. Omit it to create a template. | | `name` | `string` | Non-blank template name. Required for creation. Maximum length: 100 characters. | | `description` | `string` | Optional template description. Maximum length: 500 characters. | | `templateEngine` | `string` | Template engine. Currently only `HANDLEBARS` is supported. | | `content` | `string` | Complete Base64-encoded UTF-8 HTML document, including CSS and any Handlebars placeholders. When supplied in an update, it replaces the stored document. Maximum decoded size: 2 MB. | | `sampleData` | `object` | Representative JSON data used to evaluate the template. When supplied in an update, it replaces the stored object. Maximum JSON size: 2 MB. | | [`parameters`](#template-parameters) | `object` | PDF parameters used to validate, render, or save a template. Draft updates apply supplied fields as a shallow patch. Maximum JSON size: 2 MB. | | `comment` | `string` | Optional publish comment. Maximum length: 500 characters. | ### Template Parameters The `parameters` object contains the PDF parameters saved with a template version. When creating a new template, you can omit `parameters`, send an empty object (`{}`), or provide only selected fields. PDFBolt fills in the missing fields using the defaults shown below and saves the resulting parameters with the template version. When generating PDFs from the published template, the saved parameters are applied automatically. To change them for a specific PDF, send conversion parameters alongside `templateId` and `templateData`. They override the saved parameters for that request only and are not saved to the template. Use the same fields when validating, previewing, comparing, or saving a template: | Field | Type | Accepted values | New template default | |---------------------------------------------------------------|------------------|--------------------------------------------------------|-----------------------| | [`format`](/docs/parameters#format) | `string` | `Letter`, `Legal`, `Tabloid`, `Ledger`, `A0`–`A6` | `Letter` | | [`landscape`](/docs/parameters#landscape) | `boolean` | `true` or `false` | `false` | | [`waitUntil`](/docs/parameters#waituntil) | `string` | `load`, `domcontentloaded`, `networkidle`, or `commit` | `load` | | [`printBackground`](/docs/parameters#printbackground) | `boolean` | `true` or `false` | `true` | | [`displayHeaderFooter`](/docs/parameters#displayheaderfooter) | `boolean` | `true` or `false` | `false` | | [`headerTemplate`](/docs/parameters#headertemplate) | `string \| null` | Base64-encoded HTML | Not set | | [`footerTemplate`](/docs/parameters#footertemplate) | `string \| null` | Base64-encoded HTML | Not set | | [`waitForFunction`](/docs/parameters#waitforfunction) | `string \| null` | JavaScript function | See the default below | Only these eight parameters can be saved with a template version. Other Conversion API parameters can be sent when generating a PDF, but they apply only to that conversion. :::note Default `waitForFunction` When `waitForFunction` is omitted during template creation, PDFBolt saves this function: ```javascript // wait for all fonts and images to be loaded, if loading fails, try waitUntil networkidle () => { return document.readyState === 'complete' && document.fonts.status === 'loaded' && Array.from(document.images).every(img => img.complete); } ``` ::: ## Endpoint Reference ### Get Template Contract Returns the current version of the Template Contract for programmatic template management. It covers authentication, payload fields, HTML/CSS and Handlebars guidance, defaults, workflows, limits, and technical errors. No authentication is required for `GET` or `HEAD`. Use `HEAD` to check the endpoint status and response headers without returning a response body. **Methods**: `GET`, `HEAD` ```bash https://api.pdfbolt.com/v1/templates/contract ``` #### Success Example ```bash curl 'https://api.pdfbolt.com/v1/templates/contract' ``` ```http HTTP/1.1 200 OK Content-Type: application/json ``` For `GET`, the response body contains the complete Template Contract for the current version. Use this endpoint to retrieve the latest Template Contract instead of copying its rules into your integration. #### Template Contract and OpenAPI `GET /v1/templates/contract` provides PDFBolt-specific rules for creating, rendering, reviewing, and publishing templates. [OpenAPI YAML](/openapi.yaml) defines the exact endpoints, required fields, response schemas, and status codes. Use both when building a Template API integration. ### List Templates Returns the current team's templates with published and draft version details, excluding deleted templates. Results are ordered by the latest version update, newest first, and are not paginated. **Method**: `GET` ```bash https://api.pdfbolt.com/v1/templates ``` #### Success Example ```bash curl 'https://api.pdfbolt.com/v1/templates' \ -H 'PERSONAL-ACCESS-TOKEN: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' ``` ```json { "templates": [ { "id": "8f37b879-46cb-44fc-a5af-c223f5080771", "name": "Quarterly Report", "description": "Quarterly performance report", "templateEngine": "HANDLEBARS", "hasDraft": true, "publishedVersion": null, "draftVersion": { "id": 383, "status": "DRAFT", "versionNumber": 1, "createdTime": "2026-06-21T09:14:22Z", "modifiedTime": "2026-06-21T09:18:07Z" } }, { "id": "2b1124e7-7f8d-4fd9-9d0a-4f8cf0c58f98", "name": "Invoice", "description": "Monthly invoice template", "templateEngine": "HANDLEBARS", "hasDraft": true, "publishedVersion": { "id": 381, "status": "PUBLISHED", "versionNumber": 3, "createdTime": "2026-06-20T10:12:34Z", "modifiedTime": "2026-06-20T10:12:34Z" }, "draftVersion": { "id": 382, "status": "DRAFT", "versionNumber": 4, "createdTime": "2026-06-20T11:03:12Z", "modifiedTime": "2026-06-20T11:08:41Z" } } ] } ``` ### Get Template Details Returns the template's content, sample data, PDF parameters, and metadata for its active draft and latest published version. If an active draft exists, the content, sample data, and PDF parameters come from it. Otherwise, they come from the latest published version. **Method**: `GET` ```bash https://api.pdfbolt.com/v1/templates/{templateId} ``` #### Success Example ```bash curl 'https://api.pdfbolt.com/v1/templates/2b1124e7-7f8d-4fd9-9d0a-4f8cf0c58f98' \ -H 'PERSONAL-ACCESS-TOKEN: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' ``` ```json { "id": "2b1124e7-7f8d-4fd9-9d0a-4f8cf0c58f98", "name": "Invoice", "description": "Monthly invoice template", "templateEngine": "HANDLEBARS", "hasDraft": true, "content": "PCFET0NUWVBFIGh0bWw+PGh0bWw+PGJvZHk+PGgxPkludm9pY2Uge3tpbnZvaWNlTnVtYmVyfX08L2gxPjxwPnt7Y3VzdG9tZXJOYW1lfX08L3A+PC9ib2R5PjwvaHRtbD4=", "sampleData": { "invoiceNumber": "INV-1001", "customerName": "Acme Inc." }, "parameters": { "format": "A4", "landscape": false, "waitUntil": "load", "footerTemplate": "PGRpdj48c3BhbiBjbGFzcz0icGFnZU51bWJlciI+PC9zcGFuPjwvZGl2Pg==", "headerTemplate": "PGRpdj48L2Rpdj4=", "printBackground": true, "waitForFunction": "() => document.readyState === 'complete'", "displayHeaderFooter": true }, "publishedVersion": { "id": 381, "status": "PUBLISHED", "versionNumber": 3, "createdTime": "2026-06-20T10:12:34Z", "modifiedTime": "2026-06-20T10:12:34Z" }, "draftVersion": { "id": 382, "status": "DRAFT", "versionNumber": 4, "createdTime": "2026-06-20T11:03:12Z", "modifiedTime": "2026-06-20T11:08:41Z" } } ``` ### Validate Template Payload Validates a template payload without saving it or rendering a PDF. It checks required fields, Base64 content, payload size limits, the template engine, Handlebars syntax and evaluation, and supported PDF parameters. Use it before previewing or saving a draft. A Handlebars placeholder does not need a matching value in `sampleData` to pass validation. If the value is missing, the placeholder produces no output. **Method**: `POST` ```bash https://api.pdfbolt.com/v1/templates/validate ``` - **Required body fields:** `templateEngine`, `content`, `sampleData`, [`parameters`](#template-parameters) - **Optional body fields:** None #### Success Example ```json { "templateEngine": "HANDLEBARS", "content": "PCFkb2N0eXBlIGh0bWw+PGh0bWw+PGJvZHk+PGgxPkludm9pY2Uge3tpbnZvaWNlTnVtYmVyfX08L2gxPjwvYm9keT48L2h0bWw+", "sampleData": {"invoiceNumber": "INV-1001"}, "parameters": { "format": "A4", "waitUntil": "networkidle", "printBackground": true } } ``` ```bash curl 'https://api.pdfbolt.com/v1/templates/validate' \ -H 'PERSONAL-ACCESS-TOKEN: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' \ -H 'Content-Type: application/json' \ -d '{ "templateEngine": "HANDLEBARS", "content": "PCFkb2N0eXBlIGh0bWw+PGh0bWw+PGJvZHk+PGgxPkludm9pY2Uge3tpbnZvaWNlTnVtYmVyfX08L2gxPjwvYm9keT48L2h0bWw+", "sampleData": {"invoiceNumber": "INV-1001"}, "parameters": { "format": "A4", "waitUntil": "networkidle", "printBackground": true } }' ``` ```http HTTP/1.1 200 OK ``` ### Preview Template Payload Renders a template payload as a PDF without saving it as a template or draft. By default, the response contains a temporary download URL. Use `?responseFormat=pdf` to receive raw PDF bytes or `?responseFormat=json` to receive the PDF as Base64. Each successful preview render counts toward your available document conversions. **Method**: `POST` ```bash https://api.pdfbolt.com/v1/templates/preview ``` - **Required body fields:** `templateEngine`, `content`, `sampleData`, [`parameters`](#template-parameters) - **Optional body fields:** None | `responseFormat` | Returns | |------------------|---------| | `url` (default) | JSON with a temporary PDF download URL, its expiration time, and its size in MB | | `pdf` | PDF file (`application/pdf`) | | `json` | JSON with the PDF encoded as Base64 and its size in MB | #### Success Example ```json { "templateEngine": "HANDLEBARS", "content": "PCFkb2N0eXBlIGh0bWw+PGh0bWw+PGJvZHk+PGgxPkludm9pY2Uge3tpbnZvaWNlTnVtYmVyfX08L2gxPjwvYm9keT48L2h0bWw+", "sampleData": {"invoiceNumber": "INV-1001"}, "parameters": {"format": "A4", "waitUntil": "networkidle", "printBackground": true} } ``` **Temporary URL (default)** ```bash curl -D headers.txt 'https://api.pdfbolt.com/v1/templates/preview' \ -H 'PERSONAL-ACCESS-TOKEN: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' \ -H 'Content-Type: application/json' \ -d '{ "templateEngine": "HANDLEBARS", "content": "PCFkb2N0eXBlIGh0bWw+PGh0bWw+PGJvZHk+PGgxPkludm9pY2Uge3tpbnZvaWNlTnVtYmVyfX08L2gxPjwvYm9keT48L2h0bWw+", "sampleData": {"invoiceNumber": "INV-1001"}, "parameters": {"format": "A4", "waitUntil": "networkidle", "printBackground": true} }' ``` **Raw PDF** To receive raw PDF bytes, add `?responseFormat=pdf` to the URL: ```bash curl -D headers.txt 'https://api.pdfbolt.com/v1/templates/preview?responseFormat=pdf' \ -H 'PERSONAL-ACCESS-TOKEN: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' \ -H 'Content-Type: application/json' \ -d '{ "templateEngine": "HANDLEBARS", "content": "PCFkb2N0eXBlIGh0bWw+PGh0bWw+PGJvZHk+PGgxPkludm9pY2Uge3tpbnZvaWNlTnVtYmVyfX08L2gxPjwvYm9keT48L2h0bWw+", "sampleData": {"invoiceNumber": "INV-1001"}, "parameters": {"format": "A4", "waitUntil": "networkidle", "printBackground": true} }' \ -o preview.pdf ``` **Base64 JSON** To receive the PDF as Base64 in JSON, add `?responseFormat=json` to the URL: ```bash curl -D headers.txt 'https://api.pdfbolt.com/v1/templates/preview?responseFormat=json' \ -H 'PERSONAL-ACCESS-TOKEN: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' \ -H 'Content-Type: application/json' \ -d '{ "templateEngine": "HANDLEBARS", "content": "PCFkb2N0eXBlIGh0bWw+PGh0bWw+PGJvZHk+PGgxPkludm9pY2Uge3tpbnZvaWNlTnVtYmVyfX08L2gxPjwvYm9keT48L2h0bWw+", "sampleData": {"invoiceNumber": "INV-1001"}, "parameters": {"format": "A4", "waitUntil": "networkidle", "printBackground": true} }' ``` **Temporary URL (default)** ```http HTTP/1.1 200 OK Content-Type: application/json ``` ```json { "documentUrl": "https://s3.pdfbolt.com/pdfbolt_ec2950a1-f835-4be6-bab2-69490b53b1f9_2026-08-18T22-30-00Z.pdf", "expiresAt": "2026-08-19T22:30:00Z", "documentSizeMb": 0.08 } ``` Files stored in PDFBolt's temporary storage expire after 24 hours by default. Treat `documentUrl` as confidential: anyone who has the URL can download the PDF while the temporary file remains available. Download the PDF before `expiresAt`. **Raw PDF** The response body contains raw PDF bytes: ```http HTTP/1.1 200 OK Content-Type: application/pdf ``` **Base64 JSON** ```http HTTP/1.1 200 OK Content-Type: application/json ``` ```json { "documentSizeMb": 0.08, "pdfBase64": "JVBERi0xLjQK..." } ``` The `x-pdfbolt-conversion-cost` response header reports the credits charged for every response format. ### Compare Proposed Changes with the Published Version The template must have a published version. The endpoint renders the published version as `before` and the supplied template payload as `after` for visual comparison without saving the changes. By default, each successfully rendered PDF is returned as a temporary download URL. Use `?responseFormat=json` to receive both PDFs as Base64 instead. **Method**: `POST` ```bash https://api.pdfbolt.com/v1/templates/{templateId}/diff ``` - **Required body fields:** `content`, `sampleData`, [`parameters`](#template-parameters) - **Optional body fields:** `templateEngine` | `responseFormat` | Returns | |------------------|---------| | `url` (default) | `before` and `after` with temporary PDF download URLs, expiration times, sizes, and errors | | `json` | `before` and `after` with Base64-encoded PDFs, sizes, and errors | #### Success Example ```json { "content": "PCFkb2N0eXBlIGh0bWw+PGh0bWw+PGJvZHk+PGgxPkludm9pY2Uge3tpbnZvaWNlTnVtYmVyfX08L2gxPjxwPnt7Y3VzdG9tZXJOYW1lfX08L3A+PC9ib2R5PjwvaHRtbD4=", "sampleData": {"invoiceNumber": "INV-1001", "customerName": "Acme Inc."}, "parameters": {"format": "A4", "waitUntil": "networkidle", "printBackground": true} } ``` **Temporary URLs (default)** ```bash curl -D headers.txt 'https://api.pdfbolt.com/v1/templates/2b1124e7-7f8d-4fd9-9d0a-4f8cf0c58f98/diff' \ -H 'PERSONAL-ACCESS-TOKEN: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' \ -H 'Content-Type: application/json' \ -d '{ "content": "PCFkb2N0eXBlIGh0bWw+PGh0bWw+PGJvZHk+PGgxPkludm9pY2Uge3tpbnZvaWNlTnVtYmVyfX08L2gxPjxwPnt7Y3VzdG9tZXJOYW1lfX08L3A+PC9ib2R5PjwvaHRtbD4=", "sampleData": {"invoiceNumber": "INV-1001", "customerName": "Acme Inc."}, "parameters": {"format": "A4", "waitUntil": "networkidle", "printBackground": true} }' ``` **Base64 JSON** ```bash curl -D headers.txt 'https://api.pdfbolt.com/v1/templates/2b1124e7-7f8d-4fd9-9d0a-4f8cf0c58f98/diff?responseFormat=json' \ -H 'PERSONAL-ACCESS-TOKEN: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' \ -H 'Content-Type: application/json' \ -d '{ "content": "PCFkb2N0eXBlIGh0bWw+PGh0bWw+PGJvZHk+PGgxPkludm9pY2Uge3tpbnZvaWNlTnVtYmVyfX08L2gxPjxwPnt7Y3VzdG9tZXJOYW1lfX08L3A+PC9ib2R5PjwvaHRtbD4=", "sampleData": {"invoiceNumber": "INV-1001", "customerName": "Acme Inc."}, "parameters": {"format": "A4", "waitUntil": "networkidle", "printBackground": true} }' ``` **Temporary URLs (default)** ```json { "before": { "documentUrl": "https://s3.pdfbolt.com/pdfbolt_06192879-a2fc-490a-9f19-cc14c8baa7f7_2026-08-18T22-30-00Z.pdf", "expiresAt": "2026-08-19T22:30:00Z", "documentSizeMb": 0.1, "error": null }, "after": { "documentUrl": "https://s3.pdfbolt.com/pdfbolt_5d9c6d2f-c205-42c4-9038-2165061cba63_2026-08-18T22-30-01Z.pdf", "expiresAt": "2026-08-19T22:30:01Z", "documentSizeMb": 0.1, "error": null } } ``` In URL responses, `documentUrl`, `expiresAt`, and `documentSizeMb` are `null` for a side that fails to render. Temporary files expire after 24 hours by default. Treat each `documentUrl` as confidential: anyone who has the URL can download the PDF while the temporary file remains available. Download both PDFs before their respective `expiresAt` values. **Base64 JSON** ```json { "before": { "documentSizeMb": 0.1, "pdfBase64": "JVBERi0xLjQK...", "error": null }, "after": { "documentSizeMb": 0.1, "pdfBase64": "JVBERi0xLjQK...", "error": null } } ``` The response remains HTTP `200` even if one or both renders fail, so check `before.error` and `after.error`. The `x-pdfbolt-conversion-cost` response header reports the credits charged for successful renders only. If both renders fail, its value is `0`. ### Create or Update a Template Draft Creates a new template and its first draft when `templateId` is omitted, or saves changes to an existing template when `templateId` is provided. Every save validates the complete draft, including preserved fields, without rendering a PDF or consuming conversion credits. Make one change at a time to a template. Wait for each save or publish request to finish before making another change through the Template API or Dashboard Template Designer. Concurrent changes, including changes made by another user, may overwrite one another. **Method**: `POST` ```bash https://api.pdfbolt.com/v1/templates/drafts ``` #### Create a New Template Omit `templateId` to create a new template and its first draft. If you omit `parameters` or provide only some fields, PDFBolt fills in the rest using the [template defaults](#template-parameters). An omitted or `null` `description` is saved as an empty string. - **Required body fields:** `name`, `templateEngine`, `content`, `sampleData` - **Optional body fields:** `description`, [`parameters`](#template-parameters) ```json { "name": "Invoice", "description": "Monthly invoice template", "templateEngine": "HANDLEBARS", "content": "PCFkb2N0eXBlIGh0bWw+PGh0bWw+PGJvZHk+PGgxPkludm9pY2Uge3tpbnZvaWNlTnVtYmVyfX08L2gxPjwvYm9keT48L2h0bWw+", "sampleData": {"invoiceNumber": "INV-1001"}, "parameters": {"format": "A4", "waitUntil": "networkidle", "printBackground": true} } ``` ```bash curl 'https://api.pdfbolt.com/v1/templates/drafts' \ -H 'PERSONAL-ACCESS-TOKEN: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' \ -H 'Content-Type: application/json' \ -d '{ "name": "Invoice", "description": "Monthly invoice template", "templateEngine": "HANDLEBARS", "content": "PCFkb2N0eXBlIGh0bWw+PGh0bWw+PGJvZHk+PGgxPkludm9pY2Uge3tpbnZvaWNlTnVtYmVyfX08L2gxPjwvYm9keT48L2h0bWw+", "sampleData": {"invoiceNumber": "INV-1001"}, "parameters": {"format": "A4", "waitUntil": "networkidle", "printBackground": true} }' ``` ```json { "templateId": "93fee603-cb2a-40db-8deb-b2e3cb1eed0f", "draftVersionId": 2012, "versionNumber": 1, "createdTemplate": true } ``` #### Update an Existing Template Include `templateId` and at least one field to update. PDFBolt uses the active draft as the base, or the published version if no active draft exists: - Omitted top-level fields (`name`, `description`, `templateEngine`, `content`, `sampleData`, and `parameters`) preserve their current values. - Supplied `content` replaces the complete HTML document. - Supplied `sampleData` replaces the complete JSON object. It is not deep-merged. - The `parameters` object is applied as a shallow patch. Include only the fields you want to update. Omitted fields are preserved. An empty `parameters` object does not change any parameters and does not count as an update. - Within `parameters`, set `headerTemplate`, `footerTemplate`, or `waitForFunction` to `null` to remove the saved value. To update `format`, `landscape`, `waitUntil`, `printBackground`, or `displayHeaderFooter`, provide a concrete value. - `description: ""` clears the description. - Explicit `null` is rejected for every top-level update field. PDFBolt updates an active draft in place, preserving its `draftVersionId` and `versionNumber`. If only a published version exists, it creates the next draft version from it. - **Required body fields:** `templateId` and at least one field to update - **Updatable fields:** `name`, `description`, `templateEngine`, `content`, `sampleData`, [`parameters`](#template-parameters) Partial updates are supported only when saving a draft for an existing template. Validate, preview, and diff still require complete template payloads. ```json { "templateId": "93fee603-cb2a-40db-8deb-b2e3cb1eed0f", "parameters": {"landscape": true} } ``` ```bash curl 'https://api.pdfbolt.com/v1/templates/drafts' \ -H 'PERSONAL-ACCESS-TOKEN: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' \ -H 'Content-Type: application/json' \ -d '{ "templateId": "93fee603-cb2a-40db-8deb-b2e3cb1eed0f", "parameters": {"landscape": true} }' ``` ```json { "templateId": "93fee603-cb2a-40db-8deb-b2e3cb1eed0f", "draftVersionId": 2012, "versionNumber": 1, "createdTemplate": false } ``` `createdTemplate` indicates whether the request created the template itself. It is `false` whenever `templateId` is provided. ### Publish a Template Draft Publishes the template's active draft. A JSON object is required. Send `{}` to publish without a comment. **Method**: `POST` ```bash https://api.pdfbolt.com/v1/templates/{templateId}/publish ``` - **Required body fields:** None (a JSON body is still required and may be empty) - **Optional body fields:** `comment` (maximum 500 characters) #### Success Example ```json { "comment": "Ready for production" } ``` ```bash curl 'https://api.pdfbolt.com/v1/templates/93fee603-cb2a-40db-8deb-b2e3cb1eed0f/publish' \ -H 'PERSONAL-ACCESS-TOKEN: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' \ -H 'Content-Type: application/json' \ -d '{"comment": "Ready for production"}' ``` ```json { "templateId": "93fee603-cb2a-40db-8deb-b2e3cb1eed0f", "publishedVersionId": 2012, "versionNumber": 1 } ``` Publishing makes the active draft the new published version. ## Generate PDFs from a Published Template To generate PDFs, send the template's `templateId` and document-specific `templateData` to [`/v1/direct`](/docs/api-endpoints/direct), [`/v1/sync`](/docs/api-endpoints/sync), or [`/v1/async`](/docs/api-endpoints/async). Authenticate with an `API-KEY`. The Conversion API always uses the latest published version, even if a newer draft exists. Add [conversion parameters](/docs/parameters) to override the saved template defaults for that request. The overrides are not saved to the template. ```json { "templateId": "93fee603-cb2a-40db-8deb-b2e3cb1eed0f", "templateData": { "invoiceNumber": "INV-1001", "customerName": "Acme Inc." } } ``` ```bash curl 'https://api.pdfbolt.com/v1/direct' \ -H 'API-KEY: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' \ -H 'Content-Type: application/json' \ -d '{ "templateId": "93fee603-cb2a-40db-8deb-b2e3cb1eed0f", "templateData": { "invoiceNumber": "INV-1001", "customerName": "Acme Inc." } }' \ --output invoice.pdf ``` A successful request returns `200 OK` with raw PDF bytes: ```http HTTP/1.1 200 OK Content-Type: application/pdf x-pdfbolt-conversion-cost: 1 [binary PDF data] ``` The cURL example saves the response body as `invoice.pdf`. See the [OpenAPI YAML](/openapi.yaml) for the complete request schemas. ## Billing and Rate Limits Each successful preview render counts toward your available document conversions. For a diff, each successfully rendered side counts separately. These renders use the team's plan-based Conversion API limits and appear in Dashboard logs and usage as **Preview Conversions**. Other Template API operations do not count toward document conversions. Listing, retrieving, validating, saving, and publishing templates use separate fixed per-user management limits. Their `429` responses include `Retry-After`. Preview and diff follow the Conversion API retry behavior. Public `GET` and `HEAD` requests to `/v1/templates/contract` are not rate limited. See [Rate Limits](/docs/rate-limits) for current limits, response headers, and retry guidance. ## Error Handling Application-level Template API errors use the standard PDFBolt JSON response format: ```json { "timestamp": "2026-07-13T12:00:00Z", "httpErrorCode": 400, "errorCode": "BAD_REQUEST", "errorMessage": "Template validation failed: Field 'content' is required. Send a Base64-encoded UTF-8 HTML document as a string." } ``` Read `errorMessage` for the specific cause. See [Error Handling](/docs/error-handling) for shared error codes and retry guidance, and the [OpenAPI YAML](/openapi.yaml) for the responses documented for each endpoint. Request-level failures return non-2xx responses. Preview rendering failures also return non-2xx responses. Diff rendering failures are returned differently: after request validation succeeds, the endpoint returns HTTP `200` even if one or both renders fail. Check `before.error` and `after.error` before treating the comparison as successful. ## Next Steps --- ## PDF Generation API Parameters The Conversion API provides parameters for customizing PDFs generated from HTML, URLs, and dynamic templates. This section explains each parameter and includes usage examples. :::note Endpoint-specific parameters This page lists **common parameters** shared across all Conversion API endpoints. For endpoint-specific parameters (`isEncoded`, `customS3PresignedUrl`, `webhook`, `additionalWebhookHeaders`, `retryDelays`), see the endpoint references: - [`/v1/direct`](/docs/api-endpoints/direct) – immediate PDF response. - [`/v1/sync`](/docs/api-endpoints/sync) – URL-based access. - [`/v1/async`](/docs/api-endpoints/async) – background processing with webhook. ::: ## Source Parameters :::info Content Source You must provide **exactly one** of the following content sources: [`html`](/docs/parameters#html), [`url`](/docs/parameters#url), or [`templateId`](/docs/parameters#templateid) (with [`templateData`](/docs/parameters#templatedata)). These parameters are mutually exclusive – use only one per API request. ::: ### html **Type:** `string` **Required:** No **Details:** Accepts Base64-encoded HTML content, allowing you to provide the HTML directly for generating a PDF. **Usage:** ```json { "html": "PGh0bWw+Cjxib2R5Pgo8cD5UZXN0IHBhcmFncmFwaDwvcD4KPC9ib2R5Pgo8L2h0bWw+Cg==" } ``` This example generates a PDF containing the text: “Test paragraph”. **Related reading:** [Optimizing HTML for Professional PDF Output](/blog/optimizing-html-for-pdf). ### url **Type:** `string` **Required:** No **Details:** Accepts any valid HTTPS URL up to 2048 characters. Specifies the webpage to be converted to a PDF. PDFBolt rejects non‑HTTPS URLs and test domains (e.g., `.test`). **Usage:** ```json { "url": "https://example.com" } ``` ### templateId **Type:** `string` **Required:** No **Details:** Specifies the unique identifier (UUID) of a published template to use for PDF generation. When using this parameter, [`templateData`](/docs/parameters#templatedata) must also be provided to populate the template variables. Templates allow you to separate design from data, making document generation more efficient and maintainable. When a request includes a [PDF parameter](/docs/api-endpoints/template-api#template-parameters) that was saved when the template was created or updated, the request value takes priority over the saved value for that conversion. For example, you can override `format`, `landscape`, `waitUntil`, or `printBackground`. You can also provide Conversion API parameters that are not saved with template versions, such as `margin`, `scale`, `compression`, or `printProduction`. These request parameters do not change the template version. **Usage:** ```json { "templateId": "c2b5f574-19bc-4e34-9049-566176e6dc48", "templateData": { "customer_name": "John Doe", "invoice_number": "INV-001" } } ``` :::info Template management - Create and publish templates in the [Dashboard Template Designer](https://app.pdfbolt.com/templates) or through the [Template API](/docs/api-endpoints/template-api). - Learn more about [template creation and management](/docs/dashboard/templates). - Need a template fast? [Generate one with AI](/docs/ai-pdf-template-generation) from a description or reference files. ::: ### templateData **Type:** `object` **Required:** No (mandatory only if `templateId` is provided) **Details:** Contains the **JSON data** that replaces placeholder variables in your template to generate the final PDF. Property names in this object must match the variable names used in your template's Handlebars syntax. For example, if your template contains `{{customer_name}}`, your templateData should include a `customer_name` property with the actual value. **Usage:** ```json { "templateId": "c2b5f574-19bc-4e34-9049-566176e6dc48", "templateData": { "invoice_number": "INV-001", "customer_name": "John Doe", "items": [ { "name": "Premium Service", "price": "199.99" } ], "total": "199.99", "is_paid": true } } ``` :::note Data Privacy Template data is used only to generate the requested PDF. In request logs, the `html` and `templateData` fields are always redacted after processing is complete. **Your data remains private and secure.** See our [Privacy Policy](/privacy) and [DPA](/data-processing-agreement) for data protection details. ::: ## Page Parameters ### emulateMediaType **Type:** `string` **Required:** No **Default Value:** `print` **Details:** Determines which CSS media type PDFBolt uses when rendering the page for PDF generation. **Allowed Values:** | **Value** | **Description** | |-----------|-----------------------------------------------------------------------| | `screen` | Uses CSS screen styles, similar to how the page appears in a browser. | | `print` | Uses CSS print styles, intended for printed output. | **Usage:** ```json { "url": "https://example.com", "emulateMediaType": "screen" } ``` ### javaScriptEnabled **Type:** `boolean` **Required:** No **Default Value:** `true` **Details:** Determines whether JavaScript execution is enabled while rendering the source content for PDF generation. | **Value** | **Description** | |-----------|---------------------------------------------------------------------------------------------------------------------------| | `true` | Enables JavaScript execution so dynamic content can render. | | `false` | Disables JavaScript execution. This may improve performance, but dynamic or interactive content may not render correctly. | **Usage:** ```json { "url": "https://example.com", "javaScriptEnabled": true } ``` ### httpCredentials **Type:** `object` **Required:** No **Details:** Provides a `username` and `password` for source URLs that require HTTP Basic Authentication. | **Property Name** | **Type** | **Description** | |-------------------|----------|----------------------------------------------------| | `username` | `string` | Username for HTTP Basic Authentication. `required` | | `password` | `string` | Password for HTTP Basic Authentication. `required` | **Usage:** ```json { "url": "https://example.com", "httpCredentials": { "username": "john", "password": "pa55w0rd" } } ``` ### viewportSize **Type:** `object` **Required:** No **Details:** Defines the viewport dimensions for the page, specifying the `width` and `height` in pixels. Both `width` and `height` must be included in the `viewportSize` object and must be integers between `1` and `10000`. | **Property Name** | **Type** | **Description** | |-------------------|-----------|----------------------------------------------| | `width` | `integer` | Width of the viewport in pixels. `required` | | `height` | `integer` | Height of the viewport in pixels. `required` | **Usage:** ```json { "url": "https://example.com", "viewportSize": { "width": 1280, "height": 800 } } ``` :::note How viewport size works Viewport size controls how the page is rendered before PDF conversion, similar to resizing a browser window. This only **affects pages that use responsive design with CSS media queries** like `@media (max-width: 768px)`. Pages without defined responsive CSS will look the same regardless of viewport size. ::: ### isMobile **Type:** `boolean` **Required:** No **Default Value:** `false` **Details:** Enables mobile device emulation, including mobile viewport behavior and touch events. | **Value** | **Description** | |-----------|------------------------------------------------------------------------------------------| | `true` | Simulates a mobile device by respecting the meta viewport tag and enabling touch events. | | `false` | Disables mobile emulation, rendering the page as it would appear on a desktop device. | **Usage:** ```json { "url": "https://example.com", "isMobile": true } ``` :::note When to Use isMobile Most sites only need [`viewportSize`](/docs/parameters#viewportsize) for mobile layout. Use `isMobile: true` only for sites that detect mobile devices with JavaScript or use touch-specific CSS. ::: ### deviceScaleFactor **Type:** `number` **Required:** No **Default Value:** `1` **Details:** Sets the pixel density used when rendering the page. Higher values can make text and images appear sharper in the generated PDF. The allowed range is `1` to `4`. **Usage:** ```json { "url": "https://example.com", "deviceScaleFactor": 2 } ``` :::note Performance impact Using a higher `deviceScaleFactor` can increase the file size and slightly prolong the processing time for generating the PDF. ::: ### extraHTTPHeaders **Type:** `object` **Required:** No **Details:** Adds custom HTTP headers to requests made while rendering the source content. By default, PDFBolt sends these headers with every request the page initiates, including images, stylesheets, scripts, and other resources. You can specify up to **10 headers**. **Usage:** ```json { "url": "https://example.com", "extraHTTPHeaders": { "X-extra-header": "test1", "X-extra-header2": "test2" } } ``` :::tip Header Scope To limit headers to only the main page request and exclude sub-resources (images, CSS, JS), set [`applyExtraHTTPHeadersToAllResources`](/docs/parameters#applyextrahttpheaderstoallresources) to `false`. See details below. ::: ### applyExtraHTTPHeadersToAllResources **Type:** `boolean` **Required:** No **Default Value:** `true` **Details:** Determines whether custom headers from [`extraHTTPHeaders`](/docs/parameters#extrahttpheaders) are sent with all resource requests or only with the main page request. | **Value** | **Description** | |-----------|---------------------------------------------------------------------------------------------------------------------------| | `true` | Headers are sent with every request initiated by the page, including sub-resources like images, stylesheets, and scripts. | | `false` | Headers are sent only with the main page request. | **Usage:** ```json { "url": "https://example.com", "extraHTTPHeaders": { "X-Custom-Key": "abc123xyz789" }, "applyExtraHTTPHeadersToAllResources": false } ``` :::info Usage Notes - Use this parameter only with [`extraHTTPHeaders`](/docs/parameters#extrahttpheaders). - Set to `false` if your headers are only intended for the main page request. This prevents errors when external resources (e.g., CDNs, S3) reject custom headers, which can block assets from loading and result in a blank page. ::: ### cookies **Type:** `Array` **Required:** No **Details:** Adds cookies while rendering the source content. Each cookie must include `name`, `value`, and either `url` or both `domain` and `path` to define its scope. You can specify up to **10 cookies**. | **Property Name** | **Type** | **Description** | |-------------------|-----------|-------------------------------------------------------------------------------------------------------| | `name` | `string` | The name of the cookie. `required` | | `value` | `string` | The value of the cookie. `required` | | `url` | `string` | Specifies the URL for which the cookie is valid. `required` if `domain` and `path` are not used | | `domain` | `string` | Specifies the domain for which the cookie is valid. `required` if `url` is not used | | `path` | `string` | The URL path that must exist in the requested URL for the cookie to be sent. `required` with `domain` | | `expires` | `integer` | Expiration date of the cookie in UNIX timestamp format. `optional` | | `httpOnly` | `boolean` | Indicates if the cookie is HTTP-only. `optional` | | `secure` | `boolean` | Indicates if the cookie is secure. `optional` | **Usage:** ```json { "url": "https://example.com", "cookies": [ { "name": "sessionId", "value": "abc123", "domain": "example.com", "path": "/" }, { "name": "userPreferences", "value": "darkMode=true", "url": "https://example.com", "expires": 1739641833, "httpOnly": true, "secure": true } ] } ``` ### waitUntil **Type:** `string` **Required:** No **Default Value:** `load` **Details:** Specifies the event that defines when the page is considered fully loaded during PDF generation. Choose the value that best fits your page's content and loading behavior. **Allowed Values:** | **Value** | **Description** | |--------------------|---------------------------------------------------------------------------------------------------------| | `domcontentloaded` | Completes when the `DOMContentLoaded` event fires, meaning the HTML document has been fully parsed. | | `load` | Waits for the `load` event, which usually fires after resources such as images and scripts have loaded. | | `networkidle` | Waits until there are no active network requests for at least 500 ms. | | `commit` | Completes as soon as a network response is received, and the document begins loading. | **Usage:** ```json { "url": "https://example.com", "waitUntil": "networkidle" } ``` ### waitForFunction **Type:** `string` **Required:** No **Details:** Waits for a JavaScript function to return `true` in the page context before generating the PDF. **Usage:** ```json { "url": "https://example.com", "waitForFunction": "() => { return document.readyState === 'complete' && document.fonts.status === 'loaded' && Array.from(document.images).every(img => img.complete); }" } ``` :::note What this function does This example function waits until the document is fully loaded, all fonts are loaded, and all images have finished loading. This helps capture the page after dynamic content has loaded. ::: ### waitForSelector **Type:** `object` **Required:** No **Details:** Waits for a CSS selector to reach a specified state before generating the PDF. | **Property Name** | **Type** | **Description** | |-------------------|----------|------------------------------------------------------------------| | `selector` | `string` | CSS selector to target an element. `required` | | `state` | `string` | State to wait for. Must be one of the allowed values. `required` | **Allowed Values for `state`:** | **Value** | **Description** | |------------|---------------------------------------------------------------------------------------------------------------------------------------| | `attached` | Waits for the element to be present in the DOM. | | `detached` | Waits until the element is no longer present in the DOM. | | `visible` | Waits for the element to have a non-empty bounding box and not have `visibility: hidden`. Elements with `display: none` are excluded. | | `hidden` | Waits for the element to be either detached from the DOM, have an empty bounding box or `visibility: hidden`. Opposite of `visible`. | **Usage:** ```json { "url": "https://example.com", "waitForSelector": { "selector": "#removable-element", "state": "hidden" } } ``` :::note Invalid selector Ensure the CSS selector exists on the target page. A non-existent selector will cause a timeout error. ::: ### timeout **Type:** `integer` **Required:** No **Default Value:** `30000` **Details:** Defines the maximum time in **milliseconds** to wait for the page to load or for specific conditions to be met. The acceptable range is `1` ms up to your plan maximum: - **Free plan:** up to `30000` ms (30 seconds). - **Paid plans:** up to `60000` ms (1 minute). If the timeout is exceeded, the request will fail with a timeout error. **Usage:** ```json { "url": "https://example.com", "timeout": 30000 } ``` :::warning Common Timeout Mistake Note that the timeout value must be provided in **milliseconds**, not seconds. A common mistake is entering 30 instead of 30000 for a 30-second timeout. ::: ## PDF Layout ### format **Type:** `string` **Required:** No **Default Value:** `Letter` **Details:** Specifies the paper format for the generated PDF. If both `format` and custom page dimensions ([`width`](/docs/parameters#width) or [`height`](/docs/parameters#height)) are provided, `format` takes priority. **Allowed Values:** | **Value** | **Size in Inches** | **Size in cm** | |-----------|--------------------|-----------------| | `Letter` | 8.5in x 11in | 21.6cm x 27.9cm | | `Legal` | 8.5in x 14in | 21.6cm x 35.6cm | | `Tabloid` | 11in x 17in | 27.9cm x 43.2cm | | `Ledger` | 17in x 11in | 43.2cm x 27.9cm | | `A0` | 33.1in x 46.8in | 84cm x 118.9cm | | `A1` | 23.4in x 33.1in | 59.4cm x 84cm | | `A2` | 16.54in x 23.4in | 42cm x 59.4cm | | `A3` | 11.7in x 16.54in | 29.7cm x 42cm | | `A4` | 8.27in x 11.7in | 21cm x 29.7cm | | `A5` | 5.83in x 8.27in | 14.8cm x 21cm | | `A6` | 4.13in x 5.83in | 10.5cm x 14.8cm | **Usage:** ```json { "url": "https://example.com", "format": "A4" } ``` ### landscape **Type:** `boolean` **Required:** No **Default Value:** `false` **Details:** Defines the orientation of the generated PDF. | **Value** | **Description** | |-----------|-----------------------------------------------| | `true` | Generates the PDF in landscape mode. | | `false` | Generates the PDF in portrait mode (default). | **Usage:** ```json { "url": "https://example.com", "landscape": true } ``` ### width **Type:** `string` | `number` **Required:** No **Details:** Defines the custom page width for the generated PDF. Use this instead of `format` when you need a custom page size. You can provide a number of pixels or a string with `px`, `in`, `cm`, or `mm` units. **Supported Units:** - `px` – pixel - `in` – inch - `cm` – centimeter - `mm` – millimeter :::info Default Unit If no unit is specified, the value is interpreted as pixels. ::: **Examples:** - `"width": 100` – sets the page width to 100 pixels. - `"width": "100px"` – sets the page width to 100 pixels. - `"width": "10cm"` – sets the page width to 10 centimeters. **Usage:** ```json { "url": "https://example.com", "width": "15cm" } ``` ### height **Type:** `string` | `number` **Required:** No **Details:** Defines the custom page height for the generated PDF. Use this instead of `format` when you need a custom page size. You can provide a number of pixels or a string with `px`, `in`, `cm`, or `mm` units. If no unit is specified, the value is interpreted as pixels. **Usage:** ```json { "url": "https://example.com", "height": "800px" } ``` ### margin **Type:** `object` **Required:** No **Details:** Specifies the page margins for the generated PDF. By default, no margins are applied. Provide at least one margin property (`top`, `right`, `left`, `bottom`) – an empty margin object is treated as if `margin` was omitted. Each margin accepts a number of pixels or a string with `px`, `in`, `cm`, or `mm` units. | **Property Name** | **Type** | **Description** | |-------------------|----------------------|------------------------------| | `top` | `string` \| `number` | Specifies the top margin. | | `right` | `string` \| `number` | Specifies the right margin. | | `left` | `string` \| `number` | Specifies the left margin. | | `bottom` | `string` \| `number` | Specifies the bottom margin. | **Usage:** ```json { "url": "https://example.com", "margin": { "top": "30px", "right": "20px", "left": "20px", "bottom": "30px" } } ``` ### pageRanges **Type:** `string` **Required:** No **Details:** Specifies the page ranges to include in the PDF. Accepts a comma-separated list of page ranges or individual pages, such as `1-3, 5, 8-11`. If omitted, the PDF includes all pages. **Usage:** ```json { "url": "https://example.com", "pageRanges": "1-7" } ``` ### preferCssPageSize **Type:** `boolean` **Required:** No **Default Value:** `false` **Details:** Specifies whether the PDF generation should prioritize the CSS `@page` size defined in the content. | **Value** | **Description** | |-----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `true` | The PDF will use the dimensions specified in the `@page` CSS rule. | | `false` | The content will scale automatically to fit the page size set by [`width`](/docs/parameters#width), [`height`](/docs/parameters#height), or [`format`](/docs/parameters#format). | **Usage:** ```json { "url": "https://example.com", "preferCssPageSize": true } ``` ### printBackground **Type:** `boolean` **Required:** No **Default Value:** `false` **Details:** Specifies whether the PDF should include background graphics like colors and images. | **Value** | **Description** | |-----------|-----------------------------------------------------------------------------------| | `true` | Background graphics (e.g., colors, images) will be included in the PDF. | | `false` | Background graphics will be excluded, producing a simpler, more minimalistic PDF. | **Usage:** ```json { "url": "https://example.com", "printBackground": true } ``` :::tip For Styled PDFs Set `printBackground: true` to include CSS background colors, gradients, and background images in your PDF. Essential for invoices, certificates, dashboards, and marketing materials. ::: ### scale **Type:** `number` **Required:** No **Default Value:** `1` **Details:** Specifies the scaling factor for rendering the content in the generated PDF. The acceptable range for this parameter is between `0.1` and `2`. **Usage:** ```json { "url": "https://example.com", "scale": 1.5 } ``` ### displayHeaderFooter **Type:** `boolean` **Required:** No **Default Value:** `false` **Details:** Specifies whether to include a header and footer in the generated PDF. This parameter only takes effect when used with the [`headerTemplate`](/docs/parameters#headertemplate) and/or [`footerTemplate`](/docs/parameters#footertemplate) parameters to define their content. | **Value** | **Description** | |-----------|--------------------------------------------------------------| | `true` | Header and footer will be displayed on each page of the PDF. | | `false` | No header or footer will be included in the PDF. | **Usage:** ```json { "url": "https://example.com", "displayHeaderFooter": true, "footerTemplate": "PGRpdiBzdHlsZT0id2lkdGg6IDEwMCU7IHRleHQtYWxpZ246IGNlbnRlcjsgZm9udC1zaXplOiAxMnB4OyI+PHNwYW4gY2xhc3M9InBhZ2VOdW1iZXIiPjwvc3Bhbj4gb2YgPHNwYW4gY2xhc3M9InRvdGFsUGFnZXMiPjwvc3Bhbj48L2Rpdj4=", "margin": { "bottom": "40px" } } ``` This produces a page-number footer (e.g., "1 of 5"). See [`footerTemplate`](/docs/parameters#footertemplate) for details. **Related reading:** [Header and Footer Examples](/blog/html-to-pdf-header-footer-examples). ### headerTemplate **Type:** `string` **Required:** No **Details:** Specifies the HTML template for the page header. The template must be a valid Base64-encoded HTML string with specific classes used to dynamically inject values during PDF generation. The following classes are used to inject dynamic values: | **Class** | **Description** | |-----------------|---------------------------------------------------------------------------------| | `date` | Current date and time (e.g., "12/31/25, 5:30 PM"). | | `title` | Document title from the `` tag. | | `url` | Full URL of the source page being converted to PDF. | | `pageNumber` | Current page number in the generated PDF. | | `totalPages` | Total number of pages in the generated PDF document. | **Usage:** ```json { "url": "https://example.com", "displayHeaderFooter": true, "headerTemplate": "PGRpdiBzdHlsZT0id2lkdGg6IDEwMCU7IHRleHQtYWxpZ246IGNlbnRlcjsgZm9udC1zaXplOiAxMHB4OyBjb2xvcjogZ3JheTsiPg0KICAgIDxzcGFuIGNsYXNzPSJ0aXRsZSI+PC9zcGFuPiB8IDxzcGFuIGNsYXNzPSJ1cmwiPjwvc3Bhbj4gfCA8c3BhbiBjbGFzcz0iZGF0ZSI+PC9zcGFuPg0KPC9kaXY+DQo=", "margin": { "top": "30px" } } ``` This example generates a PDF with the following header: ```html | | ``` ### footerTemplate **Type:** `string` **Required:** No **Details:** Specifies the Base64-encoded HTML template for the page footer. Use the same conventions as the [headerTemplate](/docs/parameters#headertemplate). **Usage:** ```json { "url": "https://example.com", "displayHeaderFooter": true, "footerTemplate": "PGRpdiBzdHlsZT0id2lkdGg6IDEwMCU7IHRleHQtYWxpZ246IGNlbnRlcjsgZm9udC1zaXplOiAxMnB4OyI+DQogICAgPHNwYW4gY2xhc3M9InBhZ2VOdW1iZXIiPjwvc3Bhbj4gb2YgPHNwYW4gY2xhc3M9InRvdGFsUGFnZXMiPjwvc3Bhbj4NCjwvZGl2Pg==", "margin": { "bottom": "40px" } } ``` This example generates a PDF with the footer: ```html of ``` :::warning Header & Footer Limitations Headers and footers are rendered in an **isolated context** with no network access and no connection to the main page. Chromium treats them as simple HTML templates with limited capabilities. **What does NOT work:** - **External images** – `` will not load. - **External fonts** – Google Fonts, custom fonts via `@import` or `<link>` are not supported. - **Scripts** – `<script>` tags are ignored, JavaScript is not executed. - **External CSS** – `<link rel="stylesheet">` will not work. - **Template variables** – Handlebars syntax like `{{variableName}}` is not supported in header/footer templates. ::: :::tip What WORKS in Header & Footer - **Inline CSS** – styles directly in `style=""` attribute or within `<style>` tag. - **Inline SVG** – `<svg>...</svg>` embedded directly in the template. - **Base64 images** – ``. - **Built-in classes** – `date`, `title`, `url`, `pageNumber`, `totalPages` (use as ``). **Styling tips:** - Ensure that your HTML templates are **valid Base64-encoded strings**. - **Adjust margins** – set the `top` margin for headers and the `bottom` margin for footers to ensure visibility and avoid clipping. - **Check visibility** – verify font size and color. CSS rules defined in the body do not affect headers or footers. ::: ## Accessibility ### tagged **Type:** `boolean` **Required:** No **Default Value:** `false` **Details:** Determines whether to generate tagged PDFs that include structural information for screen readers and assistive technologies. Tagged PDFs improve accessibility but compliance with formal standards (such as PDF/UA) depends on your HTML semantics, alt text, heading order, language metadata, and reading order – `tagged: true` alone does not guarantee full conformance. | **Value** | **Description** | |-----------|--------------------------------------------------------------------------------------------------| | `true` | Generates a tagged PDF with structural markup that improves accessibility for assistive technologies. | | `false` | Generates a standard PDF without accessibility tags. | **Usage:** ```json { "url": "https://example.com", "format": "A4", "tagged": true } ``` :::note Size Considerations File sizes are typically 5-15% larger due to embedded structural information. ::: ## Print Production printProduction **Type:** `object` **Required:** No **Details:** Configures professional printing options including PDF/X standards compliance. Use this parameter when generating PDFs intended for commercial printing. Requires the Growth plan or higher. If you already have a PDF file and need a one-off conversion, use the [free PDF/X converter](/tools/free-pdfx-converter). The `printProduction` API parameter below is for PDFs generated from HTML, URLs, or templates. | **Property Name** | **Type** | **Description** | |-------------------|-----------|---------------------------------------------------------------------| | `pdfStandard` | `string` | PDF/X standard for print compliance. `optional` | | `colorSpace` | `string` | Target color space for PDF output. `optional` | | `iccProfile` | `string` | ICC color profile for RGB-to-CMYK conversion. `optional` | | `preserveBlack` | `boolean` | Preserve pure black during CMYK conversion. `optional` | **Usage:** ```json { "url": "https://example.com", "printProduction": { "pdfStandard": "pdf-x-4", "colorSpace": "cmyk", "iccProfile": "fogra39", "preserveBlack": true } } ``` ### pdfStandard **Type:** `string` **Required:** No **Details:** Generates a PDF compliant with industry-standard PDF/X specifications. PDF/X standards ensure reliable printing by embedding all necessary information and restricting features that could cause printing issues. Generated PDFs pass Adobe Preflight validation for the selected standard. **Allowed Values:** | **Value** | **PDF Version** | **Description** | |------------|-----------------|------------------------------------------------------------------------------------------------------------------| | `pdf‑x‑4` | PDF 1.6 | Modern standard supporting transparency, layers, and OpenType fonts. Recommended for most professional printing. | | `pdf‑x‑1a` | PDF 1.3 | Strictest compliance. NO transparency, NO RGB colors, NO layers. Maximum compatibility with legacy printing equipment. | **Usage:** ```json { "url": "https://example.com", "format": "A4", "printBackground": true, "printProduction": { "pdfStandard": "pdf-x-4" } } ``` :::info Automatic CMYK Conversion Using `pdfStandard` automatically converts all RGB colors to **CMYK** using the specified ICC profile, ensuring optimal results on commercial printing presses. ::: :::tip Choosing Between PDF/X-4 and PDF/X-1a **PDF/X-4** (recommended): - Preserves transparency (drop shadows, gradients, opacity). - Supports OpenType fonts. - Modern printing workflows and equipment. - Ideal for designs with CSS shadows, gradients, or transparency effects. - Learn more about PDF/X-4 → **PDF/X-1a** (maximum compatibility): - Flattens all transparency. - Converts to older PDF format. - Compatible with legacy printing systems. - Use when your print shop specifically requires it. - Learn more about PDF/X-1a → For a general overview of PDF/X standards, see PDF/X on Wikipedia. ::: :::warning Transparency in PDF/X-1a When using `pdf-x-1a` with designs containing transparency (CSS shadows, gradients, opacity), elements will be flattened. This may cause visible artifacts or "stitching" lines at edges. Additionally, text involved in transparency effects may be outlined (converted to vector paths) or rasterized (converted to pixels), making it non-selectable and non-searchable in the resulting PDF. For designs with transparency effects, `pdf-x-4` is recommended. ::: ### colorSpace **Type:** `string` **Required:** No **Default Value:** `rgb` **Details:** Sets the target color space for the generated PDF. Use `cmyk` when producing files for professional printing, or keep the default `rgb` for digital viewing (screens, emails, web). **Allowed Values:** | **Value** | **Description** | |-----------|---------------------------------------------------------------------------------------------------------| | `rgb` | RGB color space (default). Best for digital viewing – screens, emails, and web distribution. | | `cmyk` | CMYK color space. Required for professional and commercial printing. Converts all colors from RGB to CMYK. | **Usage:** ```json { "url": "https://example.com", "format": "A4", "printBackground": true, "printProduction": { "colorSpace": "cmyk" } } ``` :::info When to Use CMYK Choose `cmyk` when your PDF will be sent to a commercial printer. **CMYK** (Cyan, Magenta, Yellow, Key/Black) matches the four-ink process used by printing presses, ensuring colors reproduce accurately on paper. For PDFs viewed only on screens, `rgb` provides a wider color gamut and smaller file size. Learn more about the CMYK color model. ::: ### iccProfile **Type:** `string` **Required:** No **Default Value:** `fogra39` **Details:** Specifies the ICC color profile used for RGB-to-CMYK conversion. Each profile is calibrated for specific printing conditions (paper type, ink coverage, regional standards). The profile defines how RGB colors are mapped to CMYK values. If you provide `iccProfile`, you must also set [`colorSpace`](/docs/parameters#colorspace) to `cmyk`. **Allowed Values:** | **Value** | **Profile Name** | **Description** | |-----------|----------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `fogra39` | Coated FOGRA39 (ISO 12647-2:2004) | European standard for coated paper. The most widely used profile in Europe. ICC Registry | | `fogra51` | PSO Coated v3 (FOGRA51) | Modern European standard. Updated replacement for FOGRA39 with improved color accuracy. ICC Registry | | `swop` | SWOP 2006 Coated 5 | North American standard for web offset printing on coated paper. Idealliance SWOP | | `gracol` | GRACoL 2006 Coated 1 | North American standard for commercial printing on coated paper. Idealliance GRACoL | **Usage:** ```json { "url": "https://example.com", "format": "A4", "printBackground": true, "printProduction": { "colorSpace": "cmyk", "iccProfile": "fogra51" } } ``` :::tip Choosing an ICC Profile Printing in Europe? - Use `fogra51` for modern workflows, or `fogra39` for maximum compatibility with existing European print infrastructure. Printing in North America? - Use `gracol` for commercial sheet-fed printing, or `swop` for web offset printing (magazines, catalogs). ::: ### preserveBlack **Type:** `boolean` **Required:** No **Default Value:** `true` **Details:** Controls how pure black (`#000000` / `rgb(0,0,0)`) is handled during CMYK conversion. When enabled, pure black is mapped directly to 100% K (Key) ink only (`C:0 M:0 Y:0 K:100`), producing crisp text and sharp lines. When disabled, black is converted through the ICC profile into a "rich black" mix of all four inks (e.g., `C:75 M:68 Y:67 K:90`). Requires [`colorSpace`](/docs/parameters#colorspace) set to `cmyk` (or [`pdfStandard`](/docs/parameters#pdfstandard) provided). **Allowed Values:** | **Value** | **Description** | |-----------|------------------------------------------------------------------------------------------------------| | `true` | (default) Pure black → 100% K only. Sharp, crisp black for text and fine lines. | | `false` | Pure black → Rich black through ICC profile. Deeper black for large solid areas. | **Usage:** ```json { "url": "https://example.com", "format": "A3", "printBackground": true, "printProduction": { "colorSpace": "cmyk", "preserveBlack": false } } ``` :::info K-only Black vs Rich Black - For documents that are **primarily text** or contain **fine black lines**, keep the default `true` – pure K-only black avoids registration issues and keeps text razor-sharp. This is the right choice for most documents. - Set `preserveBlack: false` when your design has **large solid black areas** (backgrounds, banners, full-bleed panels) where you want a deeper, richer black appearance. Rich black uses all four CMYK inks to produce a denser black than K-ink alone. ::: ## Output Parameters ### contentDisposition **Type:** `string` **Required:** No **Default Value:** `inline` **Details:** Controls the `Content-Disposition` header behavior for the generated PDF, determining how browsers handle the file. **Allowed Values:** | **Value** | **Description** | |--------------|------------------------------------------------------------------------------| | `inline` | The PDF is displayed directly in the browser (default behavior). | | `attachment` | The PDF is downloaded as a file, prompting the user with a *Save As* dialog. | **Usage:** ```json { "url": "https://example.com", "contentDisposition": "attachment" } ``` This will generate a PDF with the following header: ```http Content-Disposition: attachment ``` :::note Combining with filename You can use `contentDisposition` together with the [`filename`](/docs/parameters#filename) parameter to control both the download behavior and the filename: ```json { "url": "https://example.com", "contentDisposition": "attachment", "filename": "custom_file_name" } ``` **Result:** ```http Content-Disposition: attachment; filename="custom_file_name.pdf" ``` ::: ### filename **Type:** `string` **Required:** No **Details:** Specifies the filename for the generated PDF. This name will be embedded in the `Content-Disposition` header so browsers and automation tools (such as n8n, Zapier, and Make) can correctly recognize and use the specified filename when downloading or processing the PDF. **Constraints:** - Maximum length: **255 characters**. - Allowed characters: alphanumeric (`a–z`, `A–Z`, `0–9`), dots (`.`), underscores (`_`), hyphens (`-`). - If the `.pdf` extension is omitted, it will be added automatically. **Usage:** ```json { "url": "https://example.com", "filename": "custom_file_name" } ``` This will generate a PDF with the following header: ```http Content-Disposition: inline; filename="custom_file_name.pdf" ``` :::tip Integration with Automation Tools When using PDFBolt with automation platforms like **n8n, Zapier, or Make**, the `filename` parameter ensures that downloaded PDFs use your specified name instead of a generic or UUID-based filename. ::: ### compression **Type:** `string` **Required:** No **Details:** Applies compression to reduce the final PDF file size. This is most useful for image-heavy PDFs. The compression level controls the tradeoff between file size and image quality. If you already have a PDF file and want to reduce its size without writing code, use the [free PDF compressor](/tools/free-pdf-compressor). The `compression` API parameter below applies during HTML, URL, or template PDF generation. **Allowed Values:** | **Value** | **Description** | |------------|----------------------------------------------------------| | `lossless` | Downscales images only – no quality loss. | | `low` | Highest quality – largest file size. | | `medium` | Balanced quality – moderate file size. | | `high` | Lowest quality – smallest file size. | **Usage:** ```json { "url": "https://example.com", "compression": "low" } ``` :::tip When to Use Compression Compression is most effective on **image-heavy PDFs**. Text-only PDFs will see minimal size reduction. ::: :::info What does "lossless" do? The `lossless` compression level reduces file size by downscaling images to their rendered display size (×2 for print quality) without applying any JPEG quality reduction. This is useful for users who need smaller files but want to preserve original image quality. Duplicate images are also de-duplicated automatically. ::: **Related reading:** [Compress PDF via API: Reduce File Size Programmatically](/blog/compress-pdf-api). --- ## PDF API Error Handling and Troubleshooting # Error Handling This page explains PDFBolt API error responses, async conversion failures, and retry guidance. Successful responses are documented on each endpoint page: - [/v1/direct](/docs/api-endpoints/direct) – immediate PDF response. - [/v1/sync](/docs/api-endpoints/sync) – temporary download URL. - [/v1/async](/docs/api-endpoints/async) – background processing with webhook delivery. - [Template API](/docs/api-endpoints/template-api) – creating and managing reusable templates. - [/v1/usage](/docs/api-endpoints/usage-monitoring) – current plan and remaining conversions. :::info Service Status For ongoing platform issues or scheduled maintenance, check the **PDFBolt Status Page**. ::: ## HTTP Status Codes Below is a table of common HTTP status codes, their meanings, and recommended actions: | **HTTP Status Code** | **`errorCode`** | **Meaning** | **Recommended Action** | |--------------------------------|-------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **400** | `BAD_REQUEST` | The request cannot be processed due to invalid format or parameter values. | Read the `errorMessage` field – it usually names the invalid field.Fix the request and retry. | | **400** | `UNEXPECTED_ERROR` | An unexpected error occurred during conversion. | Read the `errorMessage` field for details.Verify your request parameters.If the issue persists, contact us at contact@pdfbolt.com. | | **400** | `URL_NOT_RESOLVED` | Could not resolve the URL's domain name. | Verify the URL provided in the request. | | **400** | `HTTP_RESPONSE_FAILURE` | The target page returned a non-2xx status code (e.g., 404, 500). | Verify that the requested URL or resource is correct and accessible.Verify your authentication credentials, such as username and password, if required. | | **400** | `INVALID_CREDENTIALS` | The `httpCredentials` supplied for the target page were rejected or missing. This error does not refer to your PDFBolt API key. | Verify the [`httpCredentials`](/docs/parameters#httpcredentials) for the page you are converting.For PDFBolt API key errors, see `401 UNAUTHORIZED`. | | **400** | `CUSTOM_S3_UPLOAD_ERROR` | The custom S3 storage endpoint rejected the document upload. | Check the `errorMessage` for the specific cause, such as an expired URL, invalid signature, insufficient permissions, or missing required headers.If the URL has expired, regenerate the [pre-signed URL](/docs/s3-bucket-upload#example-generating-a-pre-signed-url-in-nodejs) with a longer expiration time. | | **400** | `TARGET_CLOSED` | The target page became unavailable or was closed. | Verify the URL provided in the request.Ensure the page stays available during conversion. | | **400** | `NO_BROWSER_CONTEXT` | The internal browser engine failed to start or process the request. | Retry the request.If the issue persists, contact us at contact@pdfbolt.com. | | **400** | `PDF_PRINTING_FAILED` | The browser could not render the PDF. | Reduce the size or complexity of the source content.Optimize images and other assets.Check the page dimensions and make sure the margins leave a printable content area. | | **400** | `TEMPLATE_EVAL_ERROR` | The published Handlebars template could not be compiled or evaluated with the provided `templateData`. | Check the `errorMessage` for the specific compilation or evaluation issue.Verify the template's Handlebars syntax, block structure, and supported helpers.Missing `templateData` values render as empty text and do not cause this error. | | **401** | `UNAUTHORIZED` | PDFBolt could not authenticate the request because the required credential is missing or invalid. | For Conversion API requests, verify the API key and the `API-KEY` header.For protected Template API requests, verify the Personal Access Token and the `PERSONAL-ACCESS-TOKEN` header. | | **403** | `FORBIDDEN` | The credential is valid, but the account cannot perform this action. Common causes include no remaining conversions, a feature unavailable on the current plan, or reaching the template limit. | Read the `errorMessage` field for the specific cause.Check your plan, remaining conversions, and templates in the [Dashboard](https://app.pdfbolt.com).Upgrade your plan if you need additional features, conversions, or templates. | | **404** | `NOT_FOUND` | The requested endpoint or resource could not be found. | Verify the endpoint URL for typos and refer to the [API Endpoints](/docs/api-endpoints) for the correct URL.For Template API requests, verify the template ID. | | **405** | `BAD_REQUEST` | The endpoint does not support the HTTP method used. | Use a method listed in the `Allow` response header. | | **408** | `CONVERSION_TIMEOUT` | The conversion process timed out. | Check parameters such as [`waitForFunction`](/docs/parameters#waitforfunction), [`waitForSelector`](/docs/parameters#waitforselector), and [`timeout`](/docs/parameters#timeout) in your request.Adjust them if necessary to prevent timeouts. | | **413** | `PAYLOAD_TOO_LARGE` | The request or template data exceeds an applicable size limit. | Reduce the affected request or template field to meet the allowed limit.Conversion API: 1 MB on the Free plan and 10 MB on paid plans.Template API: separate per-field limits apply. See [Request fields](/docs/api-endpoints/template-api#request-fields). | | **413** | `PDF_SIZE_TOO_LARGE` | The generated PDF size exceeds the maximum allowed size. Free plan limit: 2 MB per PDF. No size limit on paid plans. | Use [`compression`](/docs/parameters#compression) to reduce PDF size.Free plan: keep generated PDFs under 2 MB.Paid plans: no size limit. Each 5 MB of generated PDF is charged as 1 document. | | **415** | `BAD_REQUEST` | The request `Content-Type` is not supported. | Use a media type listed in the `Accept` response header.For Template API requests with a JSON body, send `Content-Type: application/json`. | | **422** | `UNPROCESSABLE_ENTITY` | PDFBolt could not process the request because of an unsupported edge case or an internal issue. | Retry once if the request is valid.If the same error persists, contact us at contact@pdfbolt.com with the timestamp, endpoint, and `errorMessage`. | | **429** | `TOO_MANY_REQUESTS` | The applicable Conversion API or Template API request limit was exceeded. | For Conversion API and Template API rendering requests, use exponential backoff with jitter; these responses do not include `Retry-After`.For Template API management requests, wait for the `Retry-After` delay and add jitter before retrying.See [Rate Limits](/docs/rate-limits) for endpoint-specific limits. | | **499** | `CLIENT_DISCONNECTED` | The client disconnected before PDFBolt could fully deliver the response. | Check your client, proxy, or gateway timeout settings.Keep the connection open until the response completes.For long‑running conversions, use [`/v1/async`](/docs/api-endpoints/async). | | **5xx** | `SERVICE_UNAVAILABLE` / `GATEWAY_TIMEOUT` | A server-side or infrastructure error occurred. The response may use a different body or no body. | Retry the request with exponential backoff.Check the Status Page for ongoing incidents or maintenance.If the issue persists, contact us at contact@pdfbolt.com with the details. | ## Error Response Format Application-level errors use the JSON format below. Infrastructure errors from a network, gateway, CDN, or maintenance event may return a different body or no body. Treat any HTTP `5xx` response as a transient failure and retry it, even without an `errorCode`. Error responses have the following structure: ```json { "timestamp": "2026-05-04T14:29:09Z", "httpErrorCode": 401, "errorCode": "UNAUTHORIZED", "errorMessage": "The API key is missing, invalid or has been blocked. Please verify your key or contact support." } ``` ## Error Response Fields | **Property Name** | **Type** | **Description** | |-------------------|-----------|----------------------------------------------------------------------------------------------------------| | `timestamp` | `string` | The date and time when the error occurred, in ISO 8601 format (UTC). | | `httpErrorCode` | `integer` | The HTTP status code of the error. | | `errorCode` | `string` | See the [HTTP Status Codes](/docs/error-handling#http-status-codes) table for common `errorCode` values. | | `errorMessage` | `string` | A descriptive message explaining the error. | ## Async Conversion Failures Authentication, request validation, and rate-limit errors detected before `/v1/async` accepts the request are returned immediately as HTTP errors using the JSON format above. Failures that occur after the request is accepted, such as a timeout or target page error, are delivered to your webhook with <code>status: "FAILURE"</code> and an `errorCode`. The webhook payload differs from an immediate HTTP error and does not include `httpErrorCode` or `timestamp`. Example webhook failure payload: ```json { "requestId": "a5a87a23-07b4-4bd6-8194-c150d6045c60", "status": "FAILURE", "errorCode": "URL_NOT_RESOLVED", "errorMessage": "Could not resolve the server name. Please verify the URL.", "documentUrl": null, "expiresAt": null, "isAsync": true, "duration": 550, "documentSizeMb": null, "isCustomS3Bucket": false } ``` ## Retry Guidance Use this table for errors returned as immediate HTTP responses, including errors returned before an async request is accepted. For async conversions, [`retryDelays`](/docs/api-endpoints/async#retrydelays) configures retry attempts before the final webhook is sent. If the webhook reports a final failure, use its `errorCode` to diagnose the problem. | **Status** | **Retryable** | **Strategy** | |------------|------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------| | `400` | Usually no | Check `errorMessage` and correct the request before retrying. For transient codes (`NO_BROWSER_CONTEXT`, `TARGET_CLOSED`, `UNEXPECTED_ERROR`), retry once. | | `401` | No | Verify `API-KEY` for the Conversion API or `PERSONAL-ACCESS-TOKEN` for protected Template API endpoints. | | `403` | No | Check the plan, remaining conversions, template limit, or feature availability. | | `404` | No | Correct the endpoint URL or template ID before retrying. | | `405` | No | Check the `Allow` response header and correct the HTTP method before retrying. | | `408` | Sometimes | Adjust `timeout`, `waitUntil`, or other wait parameters. | | `413` | No | Reduce the request or PDF size. Upgrade the plan only if `errorMessage` identifies a plan-specific PDF limit. | | `415` | No | Check the `Accept` response header and correct the `Content-Type` before retrying. | | `422` | Sometimes | Retry once. If the same error persists, contact PDFBolt support. | | `429` | Yes, after delay | Conversion and rendering endpoints: use exponential backoff with jitter. Template management endpoints: honor `Retry-After`, add jitter, and then retry. | | `499` | Client-dependent | Do not retry automatically. Increase client, proxy, or gateway timeouts, or use `/v1/async` for long‑running conversions. | | `5xx` | Yes | Treat it as a transient server-side failure. Retry with exponential backoff and jitter. | :::tip Troubleshooting * Read the `errorMessage` first. It usually provides specific details about what went wrong. * If you are unable to resolve the issue, use the [contact form](/contact), email us at contact@pdfbolt.com, or contact us through live chat. Include the endpoint, HTTP status, `errorCode`, and `timestamp` when available. Do not include API keys, passwords, cookies, or sensitive request data. ::: --- ## Rate Limits PDFBolt enforces separate rate limits for PDF rendering and Template API management operations. The applicable limits and retry strategy depend on the endpoint. ## Conversion API and Rendering Limits The Conversion API endpoints (`/v1/direct`, `/v1/sync`, and `/v1/async`) use limits determined by your [plan](/pricing). These limits are shared by the current team across all of its API keys. Template API preview and diff requests perform real PDF renders and count toward the same limits. ### Rate Limits per Plan | Plan | Per minute | Concurrent | |------------|-----------:|-----------:| | Free | 20 | 1 | | Basic | 100 | 5 | | Growth | 250 | 20 | | Enterprise | 500 | 50 | Need higher limits? [Contact us](/contact) for a custom plan. ### How Conversion Rate Limits Work PDFBolt uses a **sliding window** algorithm. Your usage is tracked in real time across the per-minute, per-hour, and per-day windows. When you reach any limit, further requests are temporarily blocked with HTTP **429 Too Many Requests** until your usage drops below the limit. The **concurrent request limit** restricts how many requests can be processed at the same time. Requests that exceed this limit are rejected immediately with HTTP `429`. :::info Asynchronous Endpoint For `/v1/async`, the concurrent limit applies only while the request is being accepted. After acceptance, the conversion runs in the background and no longer counts toward the concurrent request limit. ::: ### Limits in Response Headers Responses to requests that pass the conversion rate limiter include usage headers so you can monitor your limits in real time: | **Header** | **Description** | |--------------------------------|--------------------------------------------------------------| | `x-pdfbolt-limit-day` | Total allowed requests per rolling 24-hour window. | | `x-pdfbolt-limit-hour` | Total allowed requests per rolling 1-hour window. | | `x-pdfbolt-limit-minute` | Total allowed requests per rolling 1-minute window. | | `x-pdfbolt-remaining-day` | Remaining requests in the rolling 24-hour window. | | `x-pdfbolt-remaining-hour` | Remaining requests in the rolling 1-hour window. | | `x-pdfbolt-remaining-minute` | Remaining requests in the rolling 1-minute window. | ### Exceeding Conversion Rate Limits When a request exceeds one of these limits, the API returns HTTP **429 Too Many Requests** without a `Retry-After` header. Retry using exponential backoff with jitter. ## Template API Management Limits Authenticated Template API management requests are subject to per-user limits measured over rolling windows. These limits apply to listing, retrieving, validating, saving, and publishing templates. All Personal Access Tokens belonging to the same user share these limits. | Rolling window | Requests per user | `Retry-After` on `429` | |----------------|------------------:|------------------------:| | 1 minute | 360 | 1 to 60 seconds | The following Template API requests use different rate-limit rules: - `GET` and `HEAD` requests to `/v1/templates/contract` are public and are not rate limited. - `POST /v1/templates/preview` and `POST /v1/templates/{templateId}/diff` are not subject to Template API management limits. Because they render PDFs, they use the plan-based Conversion API limits described above. Successful management responses do not include the `x-pdfbolt-limit-*` or `x-pdfbolt-remaining-*` rate-limit headers. When a management limit is exceeded, the API returns HTTP **429 Too Many Requests** with a `Retry-After` header. Its value is the approximate number of whole seconds until a slot becomes available in the rolling window that rejected the request. Fractional seconds are rounded up. Wait at least the number of seconds specified by `Retry-After`, add a small random jitter, and then retry. If the retry is rejected, wait for the new `Retry-After` value before trying again. For details on this and other errors, see the [Error Handling](/docs/error-handling#http-status-codes) documentation. ## Need Higher Limits? If your application requires higher throughput or custom limits, [contact us](/contact). We offer tailored plans with higher rate limits and more concurrent requests. --- ## Templates This guide explains how PDFBolt templates work, the ways to create and publish them, and how to generate PDFs from published versions. It also includes common use cases, troubleshooting guidance, and a complete invoice example. ## What Are Templates? PDFBolt templates are reusable HTML/CSS layouts with Handlebars placeholders. Create them in the Dashboard Template Designer, generate a draft with AI, or manage them programmatically through the Template API. Once a version is published, send its `templateId` and document-specific `templateData` to the Conversion API to generate PDFs. ### Template Overview Instead of sending HTML with every request, templates separate the document design from the data supplied for each PDF: 1. **Template Definition**: Define the HTML/CSS layout and add Handlebars placeholders for dynamic values. 2. **Data Injection**: Send the template's `templateId` and document-specific `templateData` to the Conversion API. PDFBolt uses `templateData` to populate the placeholders in the latest published version, then renders the PDF. :::note Data Privacy By default, `templateData` is redacted from stored request logs after processing. See [Data Handling](/docs/privacy#data-handling) for details. **Your data remains private and secure.** ::: ### Handlebars PDFBolt uses Handlebars as its template engine. It adds dynamic values and logic to HTML: - **Variables**: `{{customer_name}}` gets replaced with actual customer data. - **Conditions**: `{{#if is_paid}}...{{/if}}` displays content when a condition is met. - **Loops**: `{{#each items}}...{{/each}}` repeats content for each item in an array. - **Nested objects**: `{{customer.address.city}}` reads a value from a nested object. :::tip Handlebars Syntax See the official Handlebars guide for more syntax and examples. ::: :::info Need Another Template Engine? We currently support Handlebars. If you need a different engine, contact us at [contact@pdfbolt.com](mailto:contact@pdfbolt.com), and we'll consider adding it based on your needs. ::: ## Choose How to Create a Template You can create a template in three ways: with the Dashboard Template Designer, AI Template Generation, or the Template API. Each option creates a draft that you can review and publish. | Creation path | How it works | Best for | |---------------|--------------|----------| | Dashboard Template Designer | Create a template from scratch or customize a design from the [template gallery](/pdf-templates). Edit its HTML and sample data, configure PDF options, and check the result with built-in previews. | Hands-on editing or starting from an existing design. | | [AI Template Generation](/docs/ai-pdf-template-generation) | Describe the document and attach reference files. PDFBolt uses AI to generate the HTML, Handlebars placeholders, sample data, and PDF parameters, then opens the draft in the Template Designer. | Creating a first draft quickly. | | [Template API](/docs/api-endpoints/template-api) | Create, validate, preview, compare, save, and publish templates programmatically with a Personal Access Token. | Automated workflows, CI, internal tools, and AI coding agents. | ## How Templates Work All three creation paths follow the same lifecycle: Create a template draft → Validate → Preview → Publish → Generate PDFs 1. **Create a template draft:** Prepare the template HTML with Handlebars variables, representative sample data, and PDF parameters. 2. **Validate and preview the draft:** Check for syntax and data errors, then render a PDF preview to verify the final output before publishing. 3. **Publish the draft:** Once the draft is ready, publish it for use by the Conversion API. 4. **Generate PDFs:** Send the `templateId` and document-specific `templateData` to one of the Conversion API endpoints: `/v1/direct`, `/v1/sync`, or `/v1/async`. 5. **Prepare the next draft:** If you need to make changes, continue editing in a new draft. PDF conversions keep using the current published version until the new draft is published. ### Simple Example: From Template to PDF This example shows how JSON data populates Handlebars variables to produce a PDF. For a detailed walkthrough, see [Template Workflow in Detail](/docs/pdf-templates#template-workflow-in-detail). **Template HTML (with Handlebars):** ```html <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Invoice #{{invoice_number}} Invoice #{{invoice_number}} Dear {{customer.name}}, {{#each items}} {{name}}: ${{price}} {{/each}} {{#if is_paid}} PAID {{else}} PENDING {{/if}} Total: ${{total}} ``` **JSON Data:** ```json { "invoice_number": "INV-001", "customer": {"name": "John Doe"}, "items": [ {"name": "Premium Service", "price": "199.99"}, {"name": "Setup Fee", "price": "99.99"} ], "is_paid": true, "total": "299.98" } ``` **Final PDF Result:** ## Why Use Templates? Templates separate reusable design from data, so you can reuse one HTML layout instead of sending it with every request. Simplified API Calls - **Cleaner code** – Send `templateId` and `templateData` instead of full HTML on every request. - **Easier debugging** – Troubleshoot the template layout and request data separately. Consistent Design - **Single source of truth** – Publish an updated version once; future API-generated PDFs use that version. - **Version history** – Compare versions or restore a previous version as a new draft. The current published version remains unchanged until you publish the draft. Faster Development - **AI-powered creation** – [Generate templates with AI](/docs/ai-pdf-template-generation) from a description or reference files. - **Ready-to-use gallery** – Start with ready-made designs for invoices, reports, certificates, resumes, and more. - **Template Designer** – Build and test templates with built-in previews. - **Template API** – Create, validate, preview, compare, save, and publish templates programmatically. - **Developer-friendly** – Use Handlebars syntax and ready-to-use code snippets. Flexible Data Handling - **Nested data** – Handlebars handles arrays and nested objects (`{{customer.address.city}}`, `{{#each line_items}}`). - **Conditional logic** – Show or hide content based on data values (`{{#if}}`, `{{#unless}}`). Team Efficiency - **Team collaboration** – Manage templates with your team. - **No infrastructure overhead** – Handle growing document volume through the API; PDFBolt manages the rendering infrastructure. ## Common Use Cases Templates work well for document workflows that combine a reusable layout with variable data. Common examples include: Business Operations Invoices, receipts, purchase orders, quotes, statements Analytics & Reporting Financial reports, KPI dashboards, performance reports, executive summaries Education & Human Resources Certificates, training records, employee handbooks, offer letters, performance evaluations Customer Communications Personalized letters, product catalogs, support tickets, shipping labels Legal & Compliance Contracts, agreements, policy documents, audit reports Sales & Marketing Sales proposals, product sheets, marketing brochures, event materials ## Template Workflow in Detail Follow these four steps to create a draft, publish it, generate a PDF, and receive the result: ### 1. Create a Template - Create a draft in the [Template Designer](/docs/dashboard/templates), [generate one with AI](/docs/ai-pdf-template-generation), or start with a design from the [template gallery](/pdf-templates). You can also create and manage drafts programmatically through the [Template API](/docs/api-endpoints/template-api). - Add Handlebars variables such as `{{variable_name}}` for dynamic values. - Use conditions such as `{{#if}}` and loops such as `{{#each}}` when needed. - Test the template with sample data in **Quick HTML Preview**, then generate a **Real PDF Preview** to verify the rendered output. - Save the draft manually or enable auto-save. :::info Learn More See the full [template creation and management guide](/docs/dashboard/templates). To work programmatically, use [`POST /v1/templates/drafts`](/docs/api-endpoints/template-api#create-or-update-a-template-draft), [`POST /v1/templates/validate`](/docs/api-endpoints/template-api#validate-template-payload), and [`POST /v1/templates/preview`](/docs/api-endpoints/template-api#preview-template-payload). Compare changes with [`POST /v1/templates/{templateId}/diff`](/docs/api-endpoints/template-api#compare-proposed-changes-with-the-published-version). ::: ### 2. Publish the Template - Publish the draft to make that version available to the Conversion API. - Copy its unique **template ID** and include it in Conversion API requests. - Use built-in **version history** to compare changes or restore a previous version. :::info Learn More See more about [template publishing](/docs/dashboard/templates#step-7-publish-the-template), or publish programmatically with [`POST /v1/templates/{templateId}/publish`](/docs/api-endpoints/template-api#publish-a-template-draft). ::: ### 3. Generate a PDF - In the Template Designer, click **Get API Code** to copy an integration snippet for the current template. - Use an official Node.js, Python, or PHP SDK, or call the REST API from any HTTP client. - Send the template's `templateId` and the JSON data for the document as `templateData`. - PDFBolt applies the data to the latest published version and renders the PDF. :::info Learn More Generate PDFs from a published template with [`POST /v1/direct`](/docs/api-endpoints/direct), [`POST /v1/sync`](/docs/api-endpoints/sync), or [`POST /v1/async`](/docs/api-endpoints/async). Explore the [SDK and integration guides](/docs/quick-start-guide#4-sdks-and-integration-guides) for code examples. ::: ### 4. Receive the Result - Receive PDF bytes, a temporary download URL, or an async webhook result, depending on the endpoint. - Download, email, or store the generated PDF. :::info Learn More See [Conversion API endpoints](/docs/api-endpoints) for response formats and delivery modes. ::: ## Before Calling the Conversion API Keep these rules in mind when generating a PDF from a template: - Authenticate the request with a conversion API key in the `API-KEY` header. - A template must have a **published version** before it can be used with the Conversion API. - `templateId` must be the template's **UUID**. Copy it from the Dashboard or retrieve it through the Template API. - When `templateId` is provided, `templateData` is **required**. - The top-level `templateData` value must be a **JSON object**, for example `{"key": "value"}`. Its properties may contain arrays and other JSON values. - Send **exactly one** content source per request: `html`, `url`, or `templateId`. - If a conversion request includes a PDF parameter that is also saved with the template, the request value takes priority over the saved value for that PDF. The template is not changed. - Templates work with all conversion endpoints: [`/v1/direct`](/docs/api-endpoints/direct), [`/v1/sync`](/docs/api-endpoints/sync), and [`/v1/async`](/docs/api-endpoints/async). ## Complete Invoice Example This example includes a complete Handlebars template, the document data sent to the Conversion API, and the generated PDF. **Template HTML:**
Example Template Code ```html Invoice - {{invoice_number}} {{#if company_logo_url}} {{/if}} {{company_name}} Invoice Invoice No: {{invoice_number}} Issue Date: {{issue_date}} {{#if due_date}} Due Date: {{due_date}} {{/if}} Billed from {{company_name}} {{#if company_address_street}} {{company_address_street}} {{/if}} {{#if company_address_line2}} {{company_address_line2}} {{/if}}{{#if company_city}} {{company_city}}{{#if company_state}}, {{company_state}}{{/if}}{{#if company_postal_code}} {{company_postal_code}}{{/if}}{{#if company_country}}, {{company_country}}{{/if}} {{/if}} {{#if company_email}} {{company_email}} {{/if}} {{#if company_phone}} {{company_phone}} {{/if}} {{#if company_tax_id}} Tax ID: {{company_tax_id}} {{/if}} Billed to {{client_name}} {{#if client_address_street}} {{client_address_street}} {{/if}} {{#if client_address_line2}} {{client_address_line2}} {{/if}}{{#if client_city}} {{client_city}}{{#if client_state}}, {{client_state}}{{/if}}{{#if client_postal_code}} {{client_postal_code}}{{/if}}{{#if client_country}}, {{client_country}}{{/if}} {{/if}} {{#if client_email}} {{client_email}} {{/if}} {{#if client_phone}} {{client_phone}} {{/if}} {{#if client_tax_id}} Tax ID: {{client_tax_id}} {{/if}} Description Quantity Price Total {{#each line_items}} {{this.description}} {{this.quantity}} {{../currency_symbol}}{{this.unit_price}} {{../currency_symbol}}{{this.total_amount}} {{/each}} Payment Information {{#if payment_info.bank_name}} Bank Name: {{payment_info.bank_name}} {{/if}} {{#if payment_info.account_number}} Account No: {{payment_info.account_number}} {{/if}} {{#if payment_info.routing_number}} Routing: {{payment_info.routing_number}} {{/if}} {{#if payment_info.payment_terms}} Terms: {{payment_info.payment_terms}} {{/if}} Subtotal {{currency_symbol}}{{subtotal_amount}} {{#if discount_amount}} Discount{{#if discount_percentage}} ({{discount_percentage}}%){{/if}} -{{currency_symbol}}{{discount_amount}} {{/if}} {{#if tax_amount}} Tax{{#if tax_percentage}} ({{tax_percentage}}%){{/if}} {{currency_symbol}}{{tax_amount}} {{/if}} Total Amount {{currency_symbol}}{{total_amount}} Thank you! ```
**Conversion Request:**
Example cURL Request ```bash curl 'https://api.pdfbolt.com/v1/direct' \ -H 'API-KEY: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' \ -H 'Content-Type: application/json' \ -d '{ "templateId": "YOUR_TEMPLATE_UUID", "templateData": { "invoice_number": "INV-2026-014", "issue_date": "June 13, 2026", "due_date": "June 27, 2026", "company_name": "Pink Brand Studio", "company_email": "pink@example.com", "company_phone": "+1 (555) 222-3344", "company_address_street": "22 Rose Garden Lane", "company_city": "Blushville", "company_state": "CA", "company_postal_code": "90210", "company_country": "USA", "company_tax_id": "PB-2026-001", "company_logo_url": "https://img.pdfbolt.com/business-logo-template.png", "client_name": "Luxe Beauty Co.", "client_email": "contact@example.com", "client_phone": "+1 (555) 667-8899", "client_address_street": "88 Glamour Ave", "client_city": "Glowtown", "client_state": "NY", "client_postal_code": "10001", "client_country": "USA", "client_tax_id": "LB-9988", "line_items": [ {"description": "Brand Identity Package", "quantity": 1, "unit_price": "600.00", "total_amount": "600.00"}, {"description": "Custom Instagram Templates", "quantity": 1, "unit_price": "200.00", "total_amount": "200.00"}, {"description": "Product Photography Session", "quantity": 1, "unit_price": "350.00", "total_amount": "350.00"}, {"description": "E-commerce Banner Design", "quantity": 2, "unit_price": "75.00", "total_amount": "150.00"}, {"description": "Email Newsletter Template", "quantity": 1, "unit_price": "120.00", "total_amount": "120.00"} ], "payment_info": { "bank_name": "Example Bank", "account_number": "1234567890123456", "routing_number": "110099001", "payment_terms": "Due in 14 days" }, "currency_symbol": "$", "subtotal_amount": "1420.00", "discount_percentage": "10", "discount_amount": "142.00", "tax_percentage": "20", "tax_amount": "255.60", "total_amount": "1533.60" } }' \ --output invoice.pdf ```
**Generated PDF:** *Generated invoice PDF with dynamic data and branded styling.* ## Troubleshooting If your request returns an error or the rendered PDF doesn't look right, check these common causes: | Problem | Cause / Fix | |---|---| | templateId is not a valid UUID | Copy the UUID from the [Dashboard](https://app.pdfbolt.com/templates) or retrieve it through the [Template API](/docs/api-endpoints/template-api). Do not use a custom name such as `my-template`. | | Template with ID ... has no active version | Publish the template draft. | | 'templateData' must also be provided | When using `templateId`, include `templateData` as a JSON object in the same request. | | Variables show as blank in the PDF | Field names in `templateData` don't match Handlebars placeholders such as `{{customer_name}}` – check spelling and case. | | Images or fonts missing in PDF | Assets must be reachable by PDFBolt's renderer (no localhost). For slow-loading assets, use [`waitUntil: "networkidle"`](/docs/parameters#waituntil). | | JavaScript-rendered content missing | Use [`waitUntil: "networkidle"`](/docs/parameters#waituntil) or [`waitForFunction`](/docs/parameters#waitforfunction). | | Header/footer variables don't work | Use Chromium placeholder classes, not Handlebars variables. See [`headerTemplate`](/docs/parameters#headertemplate) and [`footerTemplate`](/docs/parameters#footertemplate). | For a complete list of API errors and recommended actions, see [Error Handling](/docs/error-handling). ## Template FAQ
Do I need coding knowledge to use templates? Not necessarily. You can: - Use **[AI Template Generation](/docs/ai-pdf-template-generation)** to create templates from descriptions or reference files – no coding required. - Start with **[ready-made templates](/pdf-templates)** from our gallery and customize them for your brand. - **Handlebars syntax** is simple – use `{{variable_name}}` for data insertion.
Can I modify published templates? Yes. Editing a published template creates a new draft. The published version remains available to the Conversion API until you publish the draft. Use version history to compare changes or restore a previous version as a new draft.
What happens to my template data during PDF generation? Template data is used to render the PDF. After processing is complete, its value is redacted from stored request logs by default and is not stored or logged in those records. See [Data Handling](/docs/privacy#data-handling) for details.
Can I use custom fonts in my templates? Yes. Import custom fonts from **Google Fonts** or another publicly reachable HTTPS stylesheet or font URL. For example, using CSS `@import`: ```css @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'); ``` Or using a `` tag in your template's ``: ```html ```
Can I pass HTML content in template data? Yes. By default, Handlebars escapes HTML in variables for security. To render raw HTML from your data, use **triple curly braces** `{{{variable}}}` instead of double `{{variable}}`. For example: ```json { "description": "Bold text and italic" } ``` ```html {{{description}}} ``` Result in the generated PDF: **Bold text** and *italic* :::warning Use only with trusted data Triple braces bypass Handlebars escaping. User-generated HTML can change document structure, load resources, or run scripts if your template allows it. Only use `{{{variable}}}` with content you control or that has been sanitized server-side. :::
Can I use images in my templates? Yes. You can include images using: - **External URLs** – reference any publicly accessible image: `` - **Base64 encoding** – embed images directly in your template: `` - **Dynamic images** – pass image URLs in your template data using Handlebars: `` - **Inline SVG** – embed SVG graphics directly in your HTML for sharp, scalable icons and logos.
How do I control page breaks in templates? Use CSS print properties to control page breaks: ```css /* Force a page break before an element */ .new-page { page-break-before: always; } /* Prevent an element from being split across pages */ .keep-together { break-inside: avoid; } /* Force a page break after an element */ .section-end { page-break-after: always; } ``` This is especially useful for long tables or multi-section documents. See our [optimizing HTML for PDF](/blog/optimizing-html-for-pdf#control-page-breaks) guide for more examples.
Can I add headers and footers to template PDFs? Yes. Enable **Display Header & Footer** in the template's PDF Options tab. Header and footer HTML uses Chromium placeholder classes such as `pageNumber`, `totalPages`, `date`, `title`, and `url` – these are separate from Handlebars variables in the main template body. See [`headerTemplate`](/docs/parameters#headertemplate) and [`footerTemplate`](/docs/parameters#footertemplate) for the full list.
How do I style my templates? Write your styles directly in the template using one of these approaches: **` ``` **Inline styles** directly on HTML elements: ```html Invoice ``` You can combine both approaches in the same template.
Can I use JavaScript in my templates? Yes. Templates are rendered by a full browser engine, so JavaScript works. Use [`waitUntil: "networkidle"`](/docs/parameters#waituntil) to wait for all external scripts to load, or [`waitForFunction`](/docs/parameters#waitforfunction) for more precise control over when the PDF is generated. For example, generating a QR code with an external library: ```html {{#if qr_code_data}} {{/if}} ``` :::tip Safe data interpolation in JavaScript Use the built-in `{{{json value}}}` helper when embedding `templateData` into JavaScript. It serializes the value as JSON. Because the output is inserted without HTML escaping, use it only with trusted or sanitized data. :::
How can I test my template before going live? The Template Designer offers two preview modes: - **Quick HTML Preview** – instant visual feedback as you edit. - **Real PDF Preview** – generates an actual PDF using the same rendering engine as the Conversion API, so you can verify the final output including page breaks, fonts, and print styles. Your template stays in **draft** until you publish it, so testing does not affect the current published version. Successful Real PDF Preview renders use your available document conversions.
Do you support other templating engines besides Handlebars? We currently support **Handlebars**. If you need a different engine, contact us at [contact@pdfbolt.com](mailto:contact@pdfbolt.com) and we'll consider adding it based on user needs.
## Additional Resources Continue with these guides and API references: --- ## AI Template Generation AI Template Generation lets you create and edit PDF templates using prompts and reference files. Describe your requirements, attach example files, and the AI generates your complete template – HTML code, sample data, and PDF parameters (page format, orientation, header/footer). :::tip Try the AI PDF Generator See how it works and start creating templates from a prompt: [AI PDF Generator](/ai-pdf-generator). ::: ## Creating Templates with AI To create a new template with AI: **1.** Click **"Create Template"** in the Templates section of your Dashboard. **2.** Enter a **Template Name** and select the **Template Engine**. **3.** Select **"Generate with AI"** as your creation method. **4.** Enter a detailed description of the template you want to create. You can also attach reference files to give the AI more context. **5.** Click **"Generate with AI"** and let the AI create your template (typically 1-3 minutes, depending on prompt complexity and attached files). **6.** Preview the generated template in the Quick HTML Preview and refine in the Designer as needed. **7.** Generate a Real PDF Preview to check how your template looks in the final output. Successful Real PDF Preview renders use your available document conversions. **8.** [Publish the template](/docs/dashboard/templates#step-7-publish-the-template) when it's ready. Templates stay in draft until published, and only published versions can be used with the Conversion API. :::info Publish before using the Conversion API AI-generated templates start as **drafts**. Preview and refine them, then **publish** the version before using its `templateId` in Conversion API requests. After generation, you can also manage and publish the draft programmatically through the [Template API](/docs/api-endpoints/template-api). ::: ### Effective Prompts Here is an example of an effective prompt for an invoice: > *Create a professional invoice template. Header: company logo on the left, company details (name, address, phone, email, website) on the right. Below the header, a full-width row with invoice number, issue date, and due date. Two address blocks side by side: 'Bill From' and 'Bill To' – each with name, company, address, city, postal code, and country. Main section: itemized table with columns for description, quantity, unit price, and line total. Below the table: subtotal, discount (percentage and amount), tax (rate and amount), and total highlighted. At the bottom: payment terms, QR code for payment, and a short thank you message. Footer with page numbers. Clean modern styling with blue accent color.* :::tip Writing Effective Prompts The more specific your description, the better the results. Include details about: - **Layout structure** – describe sections, columns, and how elements should be positioned. - **Data fields** – specify what information to display and where (names, dates, addresses, line items, totals). - **Visual elements** – mention if you need images, logos, charts, QR codes, or barcodes. - **Styling** – indicate color scheme, font preferences, borders, or reference a style (e.g., "modern", "minimalist", "formal"). - **Page format** – specify size (A4, Letter, A6), orientation (portrait, landscape), and header/footer requirements. Prompt limit: 10,000 characters. ::: ### Attach Reference Files You can attach reference files to give the AI more details about your template: **Supported file types:** - **Documents:** PDF - **Images:** PNG, JPG, JPEG, SVG (including pasted screenshots) - **Text & Code:** HTML, CSS, JavaScript, JSON, TXT **How to attach files:** 1. Drag and drop files into the attachment area. 2. Click to browse and select files. 3. Paste from clipboard (Ctrl+V / Cmd+V). **File limits:** | Plan | Max Total Size | Max Files | |------|----------------|-----------| | Free | 1 MB | 5 | | Paid | 5 MB | 5 | :::info File privacy Reference files are used only as context for the AI operation – not stored as template assets, and not used for training AI models. ::: **Example use cases:** - Attach a PDF of an existing document you want to recreate. - Include a logo to help the AI match the template style to your branding. - Share a screenshot of a design you'd like to replicate. - Provide sample HTML/CSS for reference. ### Generated Output When the AI creates your template, you receive: | Component | Description | |-----------|-------------| | **HTML Template** | Complete HTML/CSS code with Handlebars variables. | | **Sample Data** | JSON data structure matching your template variables. | | **PDF Parameters** | Page format, orientation, print background, render waits, and optional header/footer. | AI-generated templates are fully editable. You can fine-tune them manually in the Designer or use AI Assist to refine specific sections. ## Editing Templates with AI You can use the AI to modify existing templates – whether they were AI-generated, created from scratch, or selected from the gallery. **1.** In the Template Designer, click the **AI Assist** button in the code editor toolbar. **2.** Describe the changes you want to make in the prompt field. You can also attach reference files (images, PDFs, code) to provide additional context. **3.** Click **"Prepare Changes"** and wait for the AI to modify your template. **4.** Review the AI-generated changes in the Review Modal and choose to **"Accept"** or **"Reject"** them. ### Example Edit Instructions **Adding elements:** - *Add a discount field to the invoice.* - *Add a QR code in the top right corner.* - *Include a notes/comments section after the items table.* **Styling changes:** - *Change the color scheme to blue and white.* - *Make the table headers bold and add alternating row colors.* - *Increase font size and add more spacing between sections.* **Layout modifications:** - *Move the logo to the center and make it larger.* - *Add a sidebar with contact information.* - *Split the address section into two columns.* You can make multiple edits in sequence, refining your template step by step until it matches your exact requirements. ### Review AI Changes After the AI processes your request, you'll see the **Review AI Changes** modal where you can examine and approve the changes before applying them. #### Code View Compare changes across three tabs: - **Template** – Side-by-side diff of HTML/CSS and Handlebars code showing additions, removals, and modifications. - **Data** – Changes to sample JSON data. - **Parameters** – PDF parameter changes (page format, orientation, print background, render waits, and optional header/footer). #### PDF Preview Switch to the PDF Preview tab to see before (1) and after (2) renderings side by side, helping you verify the visual result matches your expectations. Each successfully rendered PDF in the comparison uses your available document conversions. #### Accept or Reject After reviewing the changes: - Click **"Accept"** to apply the AI-generated changes to your template. - Click **"Reject"** to discard changes and return to try a different prompt. ## How AI Template Generation Works AI Template Generation uses advanced language models to understand your requirements and generate complete, functional templates. When you submit a prompt, you'll see a progress indicator with these steps: 1. **Analyzing your request** – AI reads and understands your requirements. 2. **Designing layout structure** – AI plans sections, columns, and positioning. 3. **Creating HTML template** – AI generates HTML/CSS code with Handlebars variables. 4. **Generating sample data** – AI creates JSON data matching your template. 5. **Finalizing your template** – AI completes and validates the output. :::info Generation Time Generation usually takes 1-3 minutes, depending on the complexity of your prompt, attached files, output size, and amount of sample data. ::: ## AI Generation Limits Each subscription plan includes a quota of AI template generations: | Plan | AI Generations | |------|----------------| | Free | 1 (one-time) | | Basic | 10 / month | | Growth | 50 / month | | Enterprise | 150 / month | - Each AI operation (create or edit) uses 1 generation from your quota. - A generation is consumed when the request is accepted for processing, even if processing later fails or you reject the suggested edit. - **Free plan**: 1 generation granted at registration; not renewed. - **Paid plans**: generations reset monthly with your billing cycle. - You can view your remaining generations in the Dashboard navbar. :::note Free Plan Note On the Free plan, you need to add a payment method to access AI features. The card is used for verification only – no charges are made on the Free plan. ::: ## Troubleshooting If AI generation or editing fails, check these common issues: | Problem | Cause / Fix | |---|---| | No AI generations left | Paid plans reset monthly with your billing cycle. Free plan AI generation is one-time – [upgrade your plan](/pricing) to get more. | | Payment method required | Add a payment method to your Free plan for verification – no charges are made. | | AI system is experiencing high demand | Affects the Free plan during peak usage. Try again later. | | Prompt is too long | Reduce your prompt to 10,000 characters or less. | | Total attachment size exceeds the limit | Remove files or upgrade your plan (Free: 1 MB, Paid: 5 MB total). | | Unsupported file type | Use one of: PDF, PNG, JPG/JPEG, SVG, TXT, HTML/HTM, CSS, JS, or JSON. | | Too many attachments | Attach up to 5 files per request. | | Empty prompt | Add a description of the template or edit you want the AI to create. | | Content policy violation | Revise the prompt or reference files so they comply with the usage policy, then try again. | | Template and sample data are too large for AI editing | Simplify the current template or sample data before requesting AI edits. | ## Related Resources --- ## Async Workflow Details Async conversion lets you queue PDF generation jobs and receive a signed webhook when each job finishes, so your application doesn't need to keep a request open while PDFBolt renders the document. :::info Async endpoint reference This is a conceptual overview. For full parameters and webhook payloads, see the [`/v1/async`](/docs/api-endpoints/async) endpoint reference. The `/v1/async` endpoint is available on paid plans. Free plan users can use [`/v1/direct`](/docs/api-endpoints/direct) or [`/v1/sync`](/docs/api-endpoints/sync). ::: **The diagram below shows the async conversion flow:** ## When to Use Async Conversion Use async conversion when your application doesn't need the PDF in the same HTTP response and can handle a signed webhook when the job finishes. | Use async when | Why it helps | |---------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------| | You generate PDFs in batches or background jobs. | Your application can queue work and continue without holding client requests open. | | Conversions may take longer because of large documents, heavy templates, or network-bound assets. | PDFBolt processes the job in the background and notifies your application when it finishes. | | Your application can receive public HTTPS webhooks. | The webhook notifies your application when the conversion succeeds or fails permanently. | | You want background generation with direct upload to your own S3-compatible bucket. | PDFBolt can upload the generated PDF to your `customS3PresignedUrl` and send a signed completion webhook. | For request/response flows where your application needs the PDF immediately, see [`/v1/direct`](/docs/api-endpoints/direct) or [`/v1/sync`](/docs/api-endpoints/sync). ## How the Asynchronous Flow Works ### 1. Submit Request - Send a `POST` request to the [`/v1/async`](/docs/api-endpoints/async) endpoint. - Include the required `webhook` parameter – the URL where PDFBolt will deliver the result. - For all parameters, see the [`/v1/async`](/docs/api-endpoints/async#body-parameters) endpoint reference and [conversion parameters](/docs/parameters). ```json title="Request Example:" { "url": "https://example.com", "webhook": "https://your-app.com/endpoint" } ``` ### 2. Immediate Acknowledgment - After accepting the request for background processing, the API immediately returns a `requestId`. - Requests rejected before acceptance return an HTTP error and do not trigger a webhook. - Your application can continue executing without waiting for the PDF generation to complete. ```json title="Response Example:" { "requestId": "7e075770-9c50-4018-a877-fc45c45b7850" } ``` ### 3. Background Processing - PDFBolt processes async jobs in the background, and accepted jobs may run in parallel. - New async requests are still subject to your plan's [rate limits](/docs/rate-limits) and concurrent request limits. ### 4. Direct S3 Upload (Optional) - If you provide a [`customS3PresignedUrl`](/docs/api-endpoints/async#customs3presignedurl), the generated PDF is uploaded directly to your **S3‑compatible bucket**. - If no URL is provided, the PDF is temporarily stored in PDFBolt's bucket for 24 hours. ### 5. Webhook Notification - After the conversion succeeds or all conversion attempts fail, PDFBolt attempts one `POST` request to your `webhook` URL. - Every webhook request includes an `x-pdfbolt-signature` header. See [Webhook Signature Verification](/docs/api-endpoints/async#webhook-signature-verification) for details. The webhook payload differs depending on whether you use default storage or a custom S3 bucket: ```json title="Default Storage – Success Example:" { "requestId": "a0f11722-907b-4308-be11-2c7f884f0ecc", "status": "SUCCESS", "errorCode": null, "errorMessage": null, "documentUrl": "https://s3.pdfbolt.com/pdfbolt_09366e47-2423-48c7-af8b-5eea118b49b3_2026-05-05T18-28-00Z.pdf", "expiresAt": "2026-05-06T18:28:00Z", "isAsync": true, "duration": 626, "documentSizeMb": 0.02, "isCustomS3Bucket": false } ``` ```json title="Custom S3 – Success Example:" { "requestId": "2bb49872-ff48-4c37-b104-dd5458c657cc", "status": "SUCCESS", "errorCode": null, "errorMessage": null, "documentUrl": null, "expiresAt": null, "isAsync": true, "duration": 626, "documentSizeMb": 0.02, "isCustomS3Bucket": true } ``` When `isCustomS3Bucket` is `true`, both `documentUrl` and `expiresAt` are `null` – the PDF is stored in your bucket. For the full list of webhook fields, see [webhook request parameters](/docs/api-endpoints/async#webhook-request-parameters). :::note Failure webhooks If a conversion fails permanently, PDFBolt sends a webhook with `status: "FAILURE"` and includes `errorCode` and `errorMessage`. The `documentUrl`, `expiresAt`, and `documentSizeMb` fields are `null`. See the full [failure example](/docs/api-endpoints/async#failure-example). ::: ### 6. Document Retrieval - **Default storage** – fetch the PDF from `documentUrl` before `expiresAt` (24 hours after generation). - **Custom S3:** The PDF is stored in your bucket at the location specified when you generated the pre-signed URL. ## Webhook Delivery Behavior To integrate reliably, follow these rules: - PDFBolt attempts one webhook callback per accepted conversion after the conversion succeeds or all conversion retries fail. - [`retryDelays`](/docs/api-endpoints/async#retrydelays) retries the **conversion attempt itself**, not webhook delivery. - Store the `requestId` returned by the API so you can match each webhook to the original conversion request. - If your webhook endpoint is unavailable when PDFBolt sends the callback (timeout, `5xx`, network error), the delivery is not automatically retried. Inspect your Dashboard if the webhook never arrives. - After verifying the signature, return a fast `2xx` response and run any slow follow-up work in your own background job. For the full delivery contract, see [Webhook Delivery Behavior](/docs/api-endpoints/async#webhook-delivery-behavior) in the endpoint reference. --- ## Uploading to Your S3 Bucket Use `customS3PresignedUrl` when you want PDFBolt to upload the generated PDF directly to your own S3-compatible storage. This keeps the final PDF in your storage environment, where you control retention, access policies, and downstream processing. :::info Endpoint support `customS3PresignedUrl` works with [`/v1/sync`](/docs/api-endpoints/sync) and [`/v1/async`](/docs/api-endpoints/async) on paid plans. Omit this parameter to use PDFBolt's default storage. The PDF stays available via `documentUrl` for 24 hours. ::: ## How It Works 1. Generate an HTTPS pre-signed PUT URL for the final PDF object in your S3-compatible bucket. 2. Send the URL to PDFBolt as `customS3PresignedUrl` in a `/v1/sync` or `/v1/async` request. 3. PDFBolt renders the PDF and uploads it to the pre-signed URL with an HTTP `PUT`. 4. Your application reads and manages the PDF from your own bucket. ## Pre-signed URL Requirements The URL you pass to PDFBolt must: - Use HTTPS. HTTP URLs are rejected. - Be no longer than 2048 characters. - Allow `PUT` for the exact bucket and object key. - Remain valid long enough for the full PDF conversion and upload. - Accept `Content-Type: application/pdf`. - Accept the `Content-Disposition` header PDFBolt sends during upload. By default, PDFBolt uploads with: ```http Content-Type: application/pdf Content-Disposition: inline ``` If either upload header is included in the signature, its value must exactly match the value sent by PDFBolt. When your request includes [`contentDisposition`](/docs/parameters#contentdisposition) or [`filename`](/docs/parameters#filename), generate the pre-signed URL with the matching `Content-Disposition` value. ## Supported S3-Compatible Storage PDFBolt can upload to S3-compatible storage providers that support HTTPS pre-signed `PUT` URLs, including: - Amazon S3 - Cloudflare R2 - DigitalOcean Spaces - MinIO - Wasabi - Backblaze B2 :::note More Providers Other S3-compatible providers may work if they support HTTPS pre-signed `PUT` URLs and the required upload headers. ::: ## Example: Generating a Pre-signed URL in Node.js Install the AWS SDK v3 packages: ```bash npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner ``` ```js title="generatePresignedUrl.js" const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3'); const { getSignedUrl } = require('@aws-sdk/s3-request-presigner'); const s3 = new S3Client({ region: process.env.S3_REGION || 'us-east-1', ...(process.env.S3_ENDPOINT ? { endpoint: process.env.S3_ENDPOINT } : {}), credentials: { accessKeyId: process.env.S3_ACCESS_KEY_ID, secretAccessKey: process.env.S3_SECRET_ACCESS_KEY }, // Some S3-compatible providers, such as MinIO, require path-style URLs. forcePathStyle: process.env.S3_FORCE_PATH_STYLE === 'true' }); async function generatePresignedUrl(bucketName, objectKey) { const command = new PutObjectCommand({ Bucket: bucketName, Key: objectKey, ContentType: 'application/pdf', ContentDisposition: 'inline' }); const url = await getSignedUrl(s3, command, { expiresIn: 3600 }); console.log(url); return url; } generatePresignedUrl('your-bucket-name', `pdfbolt/document-${Date.now()}.pdf`) .catch((error) => { console.error('Error generating pre-signed URL:', error); process.exit(1); }); ``` ```bash title="Run the script with your storage credentials" S3_REGION=us-east-1 \ S3_ACCESS_KEY_ID=your-access-key-id \ S3_SECRET_ACCESS_KEY=your-secret-access-key \ node generatePresignedUrl.js ``` For S3-compatible providers other than AWS S3, also set `S3_ENDPOINT`. For providers that require path-style URLs, set `S3_FORCE_PATH_STYLE=true`. The output should look like this: ```text https://your-bucket.s3.amazonaws.com/pdfbolt/document-1714580000000.pdf? ``` :::tip Pre-signed URL Best Practices - Generate a separate pre-signed URL for each PDF. Reusing the same bucket/key can overwrite an existing object. - Generate the URL on your server, not in browser code. - Keep storage credentials in environment variables or a secret manager. - Store the bucket and object key in your application so you can find the PDF later without relying on the pre-signed URL. - Set `expiresIn` long enough for the conversion to finish, especially for large pages or slow external assets. ::: ## Use the Pre-signed URL with PDFBolt ### Sync Conversion Use the generated URL as `customS3PresignedUrl` in the `/v1/sync` request body: ```json { "url": "https://example.com", "customS3PresignedUrl": "https://your-bucket.s3.amazonaws.com/pdfbolt/document.pdf?" } ``` When the upload succeeds, the response has `documentUrl: null`, `expiresAt: null`, and `isCustomS3Bucket: true` because the PDF is already in your bucket. ```json title="Sync response with custom S3" { "requestId": "09dd133a-a064-44b6-80f5-b2a7571f77ed", "status": "SUCCESS", "errorCode": null, "errorMessage": null, "documentUrl": null, "expiresAt": null, "isAsync": false, "duration": 664, "documentSizeMb": 0.02, "isCustomS3Bucket": true } ``` ### Async Conversion For `/v1/async`, include both `webhook` and `customS3PresignedUrl`: ```json { "url": "https://example.com", "webhook": "https://your-app.com/webhooks/pdfbolt", "customS3PresignedUrl": "https://your-bucket.s3.amazonaws.com/pdfbolt/document.pdf?" } ``` The initial API response returns a `requestId`. When the conversion succeeds, PDFBolt sends a webhook with `documentUrl: null`, `expiresAt: null`, and `isCustomS3Bucket: true` because the PDF is already in your bucket. ```json title="Async webhook with custom S3" { "requestId": "fadb0a9c-a5f5-4d15-8588-b15bde7c201d", "status": "SUCCESS", "errorCode": null, "errorMessage": null, "documentUrl": null, "expiresAt": null, "isAsync": true, "duration": 601, "documentSizeMb": 0.02, "isCustomS3Bucket": true } ``` See [Async Workflow Details](/docs/async-workflow) for the full async flow. ## Troubleshooting ### Upload Error When PDFBolt receives a non-2xx response while uploading the PDF to your pre-signed URL, the operation fails with errorCode: "CUSTOM_S3_UPLOAD_ERROR". For `/v1/sync`, PDFBolt returns the error in an HTTP 400 response. For `/v1/async`, PDFBolt delivers it in the final webhook with status: "FAILURE". In both cases, the `errorMessage` includes the storage provider's response code and body. This usually means one of the following: - The pre-signed URL expired before the PDF upload started. - The URL doesn't allow `PUT` for the exact bucket/key. - The signing identity doesn't have `s3:PutObject` or equivalent write permission. - The bucket or object key is wrong. - The provider rejected an upload header, usually `Content-Type` or `Content-Disposition`. - The bucket policy or provider allowlist rejects PDFBolt's upload request. If your storage provider uses IP allowlisting, see [PDFBolt IP Addresses](/docs/ip-addresses). ### Pre-signed URL Rejected Before Conversion Make sure `customS3PresignedUrl` uses HTTPS and is no longer than 2048 characters. HTTP URLs and longer URLs are rejected before PDF generation starts. ## Next Steps --- ## Dashboard After you log in, the Dashboard shows your API usage, remaining document conversions, AI generations, and the main sections of your account. :::note Admin-Only Actions Some billing and team-management actions are available only to team Admins. ::: ## Usage Counters The usage counters in the Dashboard navbar help you track remaining document conversions and AI generations. **Recurring Plan**: - **Conversions**: Displays your remaining recurring document conversions out of the total included in your plan. - **Tooltip**: Hover over the info icon to view expiration details for your recurring document conversions. **AI Generations**: - **Generations**: Shows your remaining AI template generations. - **Tooltip**: Hover over the info icon to view details about your AI generation allowance. :::info Persistent Usage Counters The usage counters stay visible as you navigate through Dashboard sections. ::: ## Usage Summary Chart The Usage Summary Chart helps you analyze your API conversion activity. The **Usage Summary** section provides: - **Daily Breakdown**: Displays the total number of successful and failed conversions for each day. - **Filter by API Key**: Filter activity by one or more Conversion API keys or by the **Preview Conversions** system API key used for previews and comparisons. - **Select a Month**: Choose the month whose conversion activity you want to view. :::info Preview Conversions The **Preview Conversions** system API key groups PDF renders from the Dashboard Template Designer, Playground, and the Template API (`preview` and `diff` requests). See [Preview Conversions](/docs/dashboard/logs#preview-conversions) for details. ::: ## Explore Dashboard Sections --- ## Playground Use the Playground to test PDF generation in your browser. Choose a template, HTML, or URL, configure conversion options, preview the PDF, and get API code for your application. :::tip Quick Testing Use the Playground to test a PDF configuration before implementing API calls in your application. ::: ## Interface Overview The Playground uses a two-panel layout: - **Left Panel**: Configuration options for source selection, PDF settings, and page behavior. - **Right Panel**: PDF preview with download option. ## Source Types Choose one PDF source: ### Template Select a saved template. The Playground uses the latest published version and its saved sample data and PDF parameters. If the template has never been published, it uses the active draft instead. - Choose a template from the dropdown list. - Preview the rendered PDF. - Generated API code uses the published template version. Publish the template or any draft changes before running the code. ### HTML Paste HTML content directly into the editor for conversion. - Write or paste HTML content. - Include inline CSS or `