# Asynchronous Request
Source: https://docs.pdfnoodle.com/api-reference/convert-html-to-pdf/asynchronous
POST /v1/html-to-pdf/async
Asynchronous means that the request will return immediately with a requestId (if the request passes validation), but the pdf file will be sent after a few seconds to your custom webhook.
We recommend using asynchronous requests for better performance on your
application.
POST [https://api.pdfnoodle.com/v1/html-to-pdf/async](https://api.pdfnoodle.com/v1/html-to-pdf/async)
## Request
```bash cURL theme={null}
curl --location 'https://api.pdfnoodle.com/v1/html-to-pdf/async' \
--header 'Authorization: Bearer pdfnoodle_api_123456789' \
--header 'Content-Type: application/json' \
--data '{
"html": "
',
'webhook': 'https://webhook.url'
}
)
result = response.json()
```
## Response
```json Immediate Response (200 OK) theme={null}
{
"requestId": "pdfnoodle_request_123456789"
}
```
```json Webhook Response (SUCCESS) theme={null}
{
"requestId": "pdfnoodle_request_123456789",
"renderStatus": "SUCCESS",
"signedUrl": "https://pdforge-production.s3.us-east-2.amazonaws.com/...",
"metadata": {
"executionTime": "1.805 seconds",
"fileSize": "4.066 kB"
}
}
```
```json Webhook Response (FAILED) theme={null}
{
"requestId": "pdfnoodle_request_123456789",
"renderStatus": "FAILED",
"signedUrl": "",
"metadata": {}
}
```
This endpoint responds with `200 OK` immediately with a `requestId`. After a few seconds, the PDF file will be generated and we will send a POST to your webhook with the response body shown above.
The `renderStatus` could be either `SUCCESS` or `FAILED`.
If the request `FAILED`, you should contact support to figure out why your PDF
generation failed.
The response body will contain a `signedUrl` key which is a temporary URL pointing to the generated PDF file on our S3 bucket. If you passed a custom `s3_bucket`, it'll be stored there instead. This URL will expire after the time specified in `signedUrlExpiresIn` (default: 1 hour).
### PDF Render Metadata
We'll also bring some additional metadata from your PDF render with every response:
* **executionTime** - Time in seconds it took to generate your PDF
* **fileSize** - PDF size in kiloBytes
## Parameters
The HTML content you want to render
The url of your webhook
The object containing the parameters for your PDF. [See all the options
here](/api-reference/options/pdf-params).
If true, will return a .PNG file instead of a .PDF file
This object containing the metadata for your PDF. [See all the options
here](/api-reference/options/pdf-metadata).
The id of the active s3 connection you want to store your generated file on.
(only available in the high plan)
The path, including subdirectories and the filename without extension, to use
when saving the render in your S3 bucket. (only available if being stored in
custom s3\_bucket)
Number of seconds that the generated signed URL will take to expire (default:
1 hour)
If true, will hide the footer and the header elements on the first page of the
generated PDF
## Checking PDF Status
Instead of waiting for the webhook, you can also check the status of your PDF generation by polling the [Get PDF Status](/api-reference/pdf-status/get) endpoint using the `requestId` returned from this request.
# Batch Request
Source: https://docs.pdfnoodle.com/api-reference/convert-html-to-pdf/batch
POST /v1/html-to-pdf/batch
Convert multiple HTML documents to PDFs in a single request. Process up to 20 PDFs at once, with optional merging into a single file.
Batch HTML to PDF allows you to convert up to 20 HTML documents into PDFs in a single API call.
You can also merge all generated PDFs into a single file.
POST [https://api.pdfnoodle.com/v1/html-to-pdf/batch](https://api.pdfnoodle.com/v1/html-to-pdf/batch)
## Request
```bash cURL theme={null}
curl --location 'https://api.pdfnoodle.com/v1/html-to-pdf/batch' \
--header 'Authorization: Bearer pdfnoodle_api_123456789' \
--header 'Content-Type: application/json' \
--data '{
"items": [
{
"html": "
'
}
],
'merge': False
}
)
result = response.json()
```
## Response
By default, the request waits for all PDFs to be generated before returning a response (synchronous behavior). If the batch takes longer than 30 seconds, it will return a `202 Accepted` response with a status URL to poll.
```json Success (200 OK) theme={null}
{
"batchRequestId": "pdfnoodle_batch_123456789",
"status": "SUCCESS",
"total": 2,
"completed": 2,
"merge": false,
"results": [
{
"requestId": "pdfnoodle_request_abc123",
"renderStatus": "SUCCESS",
"signedUrl": "https://pdforge-production.s3.us-east-2.amazonaws.com/...",
"metadata": {
"executionTime": "1.805 seconds",
"fileSize": "4.066 kB"
}
},
{
"requestId": "pdfnoodle_request_def456",
"renderStatus": "SUCCESS",
"signedUrl": "https://pdforge-production.s3.us-east-2.amazonaws.com/...",
"metadata": {
"executionTime": "2.103 seconds",
"fileSize": "5.200 kB"
}
}
]
}
```
```json Partial Success (200 OK) theme={null}
{
"batchRequestId": "pdfnoodle_batch_123456789",
"status": "PARTIAL_SUCCESS",
"total": 2,
"completed": 2,
"merge": false,
"results": [
{
"requestId": "pdfnoodle_request_abc123",
"renderStatus": "SUCCESS",
"signedUrl": "https://pdforge-production.s3.us-east-2.amazonaws.com/...",
"metadata": {
"executionTime": "1.805 seconds",
"fileSize": "4.066 kB"
}
},
{
"requestId": "pdfnoodle_request_def456",
"renderStatus": "FAILED",
"signedUrl": "",
"metadata": {}
}
]
}
```
```json Timeout (202 Accepted) theme={null}
{
"batchRequestId": "pdfnoodle_batch_123456789",
"statusUrl": "https://api.pdfnoodle.com/v1/pdf/batch/status/pdfnoodle_batch_123456789",
"message": "Batch couldn't complete within 30 seconds. Check the batch status URL for progress."
}
```
The `status` field indicates the overall outcome of the batch:
* **SUCCESS** - All PDFs were generated successfully
* **PARTIAL\_SUCCESS** - Some PDFs were generated, but at least one failed
* **FAILED** - All PDFs failed to generate
### Merged Response
When `merge` is set to `true`, the response will also include a `mergedUrl` pointing to a single PDF containing all successfully generated PDFs combined:
```json theme={null}
{
"batchRequestId": "pdfnoodle_batch_123456789",
"status": "SUCCESS",
"total": 2,
"completed": 2,
"merge": true,
"mergedUrl": "https://pdforge-production.s3.us-east-2.amazonaws.com/merged/...",
"results": [...]
}
```
## Parameters
An array of HTML to PDF conversion items. Minimum 1 item, maximum 20 items per batch.
Each item accepts the following properties:
The HTML content you want to render
The object containing the parameters for your PDF. [See all the options
here](/api-reference/options/pdf-params).
If true, will return a .PNG file instead of a .PDF file
The id of the active s3 connection you want to store your generated file on.
(only available in the high plan)
The path, including subdirectories and the filename without extension, to use
when saving the render in your S3 bucket. (only available if being stored in
custom s3\_bucket)
This object containing the metadata for your PDF. [See all the options
here](/api-reference/options/pdf-metadata).
If true, will hide the footer and the header elements on the first page of the
generated PDF
If true, will enter Debug Mode and show additional properties on the response
to help you figure out your variables and html rendered content. [Here's
everything you need to know about Debug
Mode](/api-reference/options/debug-mode)
If true, the request will return immediately with a `batchRequestId` and
`statusUrl`. You can poll the [Get Batch PDF Status](/api-reference/pdf-status/get-batch)
endpoint to track progress.
If true, all successfully generated PDFs will be merged into a single file.
The merged file URL will be available in the `mergedUrl` field of the response.
Cannot be used when any item has `convertToImage` set to `true`.
A URL to receive a POST request when the batch is complete. The webhook payload
will contain the full batch status and results.
Number of seconds that the generated signed URLs will take to expire (default:
1 hour)
## Async Mode
When `async` is set to `true`, the request returns immediately with a `batchRequestId` and a `statusUrl`:
```json theme={null}
{
"batchRequestId": "pdfnoodle_batch_123456789",
"statusUrl": "https://api.pdfnoodle.com/v1/pdf/batch/status/pdfnoodle_batch_123456789",
"total": 2,
"subRequestIds": ["pdfnoodle_request_abc123", "pdfnoodle_request_def456"],
"message": "Batch PDF generation started. Check the status URL for progress."
}
```
You can then check the status of your batch using the [Get Batch PDF Status](/api-reference/pdf-status/get-batch) endpoint.
## Payload Size Limit
The request body is limited to **3MB**. Since batch requests contain multiple items with raw HTML content, this limit can be reached quickly when sending many items with large HTML documents.
If your request exceeds this limit, you'll receive a `413` status code with the following error:
```json theme={null}
{
"message": "request entity too large"
}
```
If you're hitting the 3MB payload limit, consider splitting your batch into
multiple smaller requests. For example, instead of sending 20 items in a single
request, send two requests with 10 items each.
## Checking Batch Status
You can check the status of your batch at any time using the [Get Batch PDF Status](/api-reference/pdf-status/get-batch) endpoint with the `batchRequestId`.
To check the status of an individual PDF within the batch, use the [Get PDF Status](/api-reference/pdf-status/get) endpoint with the individual `requestId` from the results.
# Synchronous request
Source: https://docs.pdfnoodle.com/api-reference/convert-html-to-pdf/synchronous
POST /v1/html-to-pdf/sync
Synchronous means that when calling the API the request will wait for the pdf file to be generated before returning a response.
We recommend using asynchronous requests for better performance on your
application.
POST [https://api.pdfnoodle.com/v1/html-to-pdf/sync](https://api.pdfnoodle.com/v1/html-to-pdf/sync)
## Request
```bash cURL theme={null}
curl --location 'https://api.pdfnoodle.com/v1/html-to-pdf/sync' \
--header 'Authorization: Bearer pdfnoodle_api_123456789' \
--header 'Content-Type: application/json' \
--data '{
"html": "
'
}
)
result = response.json()
```
## Response
```json Success (200 OK) theme={null}
{
"signedUrl": "https://pdforge-production.s3.us-east-2.amazonaws.com/...",
"metadata": {
"executionTime": "1.805 seconds",
"fileSize": "4.066 kB"
}
}
```
```json Timeout (202 Accepted) theme={null}
{
"requestId": "pdfnoodle_request_123456789",
"statusUrl": "https://api.pdfnoodle.com/v1/pdf/status/pdfnoodle_request_123456789",
"message": "Couldn't generate PDF within 30 seconds, a queue was added to generate it asynchronously. Check the queue status on this URL."
}
```
This endpoint responds with `200 OK` once the PDF has been generated. The response body will contain a `signedUrl` key which is a temporary URL pointing to the generated PDF file on our S3 bucket. If you passed a custom `s3_bucket`, it'll be stored there instead. This URL will expire after the time specified in `signedUrlExpiresIn` (default: 1 hour).
If your PDF takes more than 30 seconds to render, it will automatically be added to an asynchronous queue and you'll receive a `202 Accepted` response with a `requestId`. You can check the status using the [Get PDF Status](/api-reference/pdf-status/get) endpoint.
### PDF Render Metadata
We'll also bring some additional metadata from your PDF render with every response:
* **executionTime** - Time in seconds it took to generate your PDF
* **fileSize** - PDF size in kiloBytes
## Parameters
The HTML content you want to render
The object containing the parameters for your PDF. [See all the options
here](/api-reference/options/pdf-params).
If true, will return a .PNG file instead of a .PDF file
This object containing the metadata for your PDF. [See all the options
here](/api-reference/options/pdf-metadata).
The id of the active s3 connection you want to store your generated file on.
(only available in the high plan)
The path, including subdirectories and the filename without extension, to use
when saving the render in your S3 bucket. (only available if being stored in
custom s3\_bucket)
Number of seconds that the generated signed URL will take to expire (default:
1 hour)
If true, will hide the footer and the header elements on the first page of the
generated PDF
## Request Timeout (>30 seconds)
Synchronous requests are useful when you want to get the results back
immediately. However, if your PDF takes more than 30 seconds to render, it
will automatically be added to an asynchronous queue.
Sometimes, your PDF might take more than 30 seconds to render, if it has a lot of heavy images/charts and a lot of pages being generated.
**If that's the case and your PDF takes more than 30 seconds to render synchronously**, we'll automatically add it to an asynchronous queue to render it.
You'll be able to check if the PDF is completed using the [Get PDF Status](/api-reference/pdf-status/get) endpoint with the requestId you just received (or just by making a GET request on the statusUrl).
If the PDF is still rendering, you'll get a response like this:
```json theme={null}
{
"requestId": "pdfnoodle_request_123456789",
"renderStatus": "ONGOING",
"signedUrl": "",
"metadata": {}
}
```
And **once the PDF is ready**, you'll receive a response like this:
```json theme={null}
{
"requestId": "pdfnoodle_request_123456789",
"renderStatus": "SUCCESS",
"signedUrl": "https://pdforge-production.s3.us-east-2.amazonaws.com/...",
"metadata": {
"executionTime": "1.805 seconds",
"fileSize": "4.066 kB"
}
}
```
# Asynchronous request
Source: https://docs.pdfnoodle.com/api-reference/generate-pdf-from-template/asynchronous
POST /v1/pdf/async
Asynchronous means that the request will return immediately with a requestId (if the request passes validation), but the pdf file will be sent after a few seconds to your custom webhook.
We recommend using asynchronous requests for better performance on your
application.
POST [https://api.pdfnoodle.com/v1/pdf/async](https://api.pdfnoodle.com/v1/pdf/async)
## Request
```bash cURL theme={null}
curl --location 'https://api.pdfnoodle.com/v1/pdf/async' \
--header 'Authorization: Bearer pdfnoodle_api_123456789' \
--header 'Content-Type: application/json' \
--data '{
"templateId": "check",
"webhook": "https://webhook.url",
"data": {
"currentDDate": "01/12/2025",
"user": {
"name": "John Doe",
"email": "john.doe@email.com"
},
"details": [{"score": "100", "description": "high score"}]
}
}'
```
```javascript JavaScript theme={null}
const response = await fetch("https://api.pdfnoodle.com/v1/pdf/async", {
method: "POST",
headers: {
Authorization: "Bearer pdfnoodle_api_123456789",
"Content-Type": "application/json",
},
body: JSON.stringify({
templateId: "check",
webhook: "https://webhook.url",
data: {
currentDDate: "01/12/2025",
user: {
name: "John Doe",
email: "john.doe@email.com",
},
details: [{ score: "100", description: "high score" }],
},
}),
});
const result = await response.json();
```
```python Python theme={null}
import requests
response = requests.post(
'https://api.pdfnoodle.com/v1/pdf/async',
headers={
'Authorization': 'Bearer pdfnoodle_api_123456789',
'Content-Type': 'application/json'
},
json={
'templateId': 'check',
'webhook': 'https://webhook.url',
'data': {
'currentDDate': '01/12/2025',
'user': {
'name': 'John Doe',
'email': 'john.doe@email.com'
},
'details': [{'score': '100', 'description': 'high score'}]
}
}
)
result = response.json()
```
## Response
```json Immediate Response (200 OK) theme={null}
{
"requestId": "pdfnoodle_request_123456789"
}
```
```json Webhook Response (SUCCESS) theme={null}
{
"requestId": "pdfnoodle_request_123456789",
"renderStatus": "SUCCESS",
"signedUrl": "https://pdforge-production.s3.us-east-2.amazonaws.com/...",
"metadata": {
"executionTime": "1.805 seconds",
"fileSize": "4.066 kB"
}
}
```
```json Webhook Response (FAILED) theme={null}
{
"requestId": "pdfnoodle_request_123456789",
"renderStatus": "FAILED",
"signedUrl": "",
"metadata": {}
}
```
This endpoint responds with `200 OK` immediately with a `requestId`. After a few seconds, the PDF file will be generated and we will send a POST to your webhook with the response body shown above.
The `renderStatus` could be either `SUCCESS` or `FAILED`.
If the request `FAILED`, you should contact support to figure out why your PDF
generation failed.
The response body will contain a `signedUrl` key which is a temporary URL pointing to the generated PDF file on our S3 bucket. If you passed a custom `s3_bucket`, it'll be stored there instead. This URL will expire after the time specified in `signedUrlExpiresIn` (default: 1 hour).
### PDF Render Metadata
We'll also bring some additional metadata from your PDF render with every response:
* **executionTime** - Time in seconds it took to generate your PDF
* **fileSize** - PDF size in kiloBytes
## Parameters
The id of your PDF template
The url of your webhook
The object containing the variables from your PDF template
If true, will return a .PNG file instead of a .PDF file
This object containing the metadata for your PDF. [See all the options
here](/api-reference/options/pdf-metadata).
The id of the active s3 connection you want to store your generated file on.
(only available in the high plan)
The path, including subdirectories and the filename without extension, to use
when saving the render in your S3 bucket. (only available if being stored in
custom s3\_bucket)
Number of seconds that the generated signed URL will take to expire (default:
1 hour)
If you're using our embedded white-label solution, you can pass the UUID of
your customer to generate the PDF of the customized template
If true, will hide the footer and the header elements on the first page of the
generated PDF
If true, will enter Debug Mode and show additional properties on the response
to help you figure out your variables and html rendered content. [Here's
everything you need to know about Debug
Mode](/api-reference/options/debug-mode)
## Checking PDF Status
Instead of waiting for the webhook, you can also check the status of your PDF generation by polling the [Get PDF Status](/api-reference/pdf-status/get) endpoint using the `requestId` returned from this request.
# Batch Request
Source: https://docs.pdfnoodle.com/api-reference/generate-pdf-from-template/batch
POST /v1/pdf/batch
Generate multiple PDFs from templates in a single request. Process up to 20 PDFs at once, with optional merging into a single file.
Batch PDF generation allows you to generate up to 20 PDFs in a single API call.
Each item in the batch can use a different template and data. You can also merge all generated PDFs into a single file.
POST [https://api.pdfnoodle.com/v1/pdf/batch](https://api.pdfnoodle.com/v1/pdf/batch)
## Request
```bash cURL theme={null}
curl --location 'https://api.pdfnoodle.com/v1/pdf/batch' \
--header 'Authorization: Bearer pdfnoodle_api_123456789' \
--header 'Content-Type: application/json' \
--data '{
"items": [
{
"templateId": "invoice",
"data": {
"invoiceNumber": "INV-001",
"customer": { "name": "John Doe" },
"total": "$150.00"
}
},
{
"templateId": "invoice",
"data": {
"invoiceNumber": "INV-002",
"customer": { "name": "Jane Smith" },
"total": "$250.00"
}
}
],
"merge": false
}'
```
```javascript JavaScript theme={null}
const response = await fetch("https://api.pdfnoodle.com/v1/pdf/batch", {
method: "POST",
headers: {
Authorization: "Bearer pdfnoodle_api_123456789",
"Content-Type": "application/json",
},
body: JSON.stringify({
items: [
{
templateId: "invoice",
data: {
invoiceNumber: "INV-001",
customer: { name: "John Doe" },
total: "$150.00",
},
},
{
templateId: "invoice",
data: {
invoiceNumber: "INV-002",
customer: { name: "Jane Smith" },
total: "$250.00",
},
},
],
merge: false,
}),
});
const result = await response.json();
```
```python Python theme={null}
import requests
response = requests.post(
'https://api.pdfnoodle.com/v1/pdf/batch',
headers={
'Authorization': 'Bearer pdfnoodle_api_123456789',
'Content-Type': 'application/json'
},
json={
'items': [
{
'templateId': 'invoice',
'data': {
'invoiceNumber': 'INV-001',
'customer': {'name': 'John Doe'},
'total': '$150.00'
}
},
{
'templateId': 'invoice',
'data': {
'invoiceNumber': 'INV-002',
'customer': {'name': 'Jane Smith'},
'total': '$250.00'
}
}
],
'merge': False
}
)
result = response.json()
```
## Response
By default, the request waits for all PDFs to be generated before returning a response (synchronous behavior). If the batch takes longer than 30 seconds, it will return a `202 Accepted` response with a status URL to poll.
```json Success (200 OK) theme={null}
{
"batchRequestId": "pdfnoodle_batch_123456789",
"status": "SUCCESS",
"total": 2,
"completed": 2,
"merge": false,
"results": [
{
"requestId": "pdfnoodle_request_abc123",
"renderStatus": "SUCCESS",
"signedUrl": "https://pdforge-production.s3.us-east-2.amazonaws.com/...",
"metadata": {
"executionTime": "1.805 seconds",
"fileSize": "4.066 kB"
}
},
{
"requestId": "pdfnoodle_request_def456",
"renderStatus": "SUCCESS",
"signedUrl": "https://pdforge-production.s3.us-east-2.amazonaws.com/...",
"metadata": {
"executionTime": "2.103 seconds",
"fileSize": "5.200 kB"
}
}
]
}
```
```json Partial Success (200 OK) theme={null}
{
"batchRequestId": "pdfnoodle_batch_123456789",
"status": "PARTIAL_SUCCESS",
"total": 2,
"completed": 2,
"merge": false,
"results": [
{
"requestId": "pdfnoodle_request_abc123",
"renderStatus": "SUCCESS",
"signedUrl": "https://pdforge-production.s3.us-east-2.amazonaws.com/...",
"metadata": {
"executionTime": "1.805 seconds",
"fileSize": "4.066 kB"
}
},
{
"requestId": "pdfnoodle_request_def456",
"renderStatus": "FAILED",
"signedUrl": "",
"metadata": {}
}
]
}
```
```json Timeout (202 Accepted) theme={null}
{
"batchRequestId": "pdfnoodle_batch_123456789",
"statusUrl": "https://api.pdfnoodle.com/v1/pdf/batch/status/pdfnoodle_batch_123456789",
"message": "Batch couldn't complete within 30 seconds. Check the batch status URL for progress."
}
```
The `status` field indicates the overall outcome of the batch:
* **SUCCESS** - All PDFs were generated successfully
* **PARTIAL\_SUCCESS** - Some PDFs were generated, but at least one failed
* **FAILED** - All PDFs failed to generate
### Merged Response
When `merge` is set to `true`, the response will also include a `mergedUrl` pointing to a single PDF containing all successfully generated PDFs combined:
```json theme={null}
{
"batchRequestId": "pdfnoodle_batch_123456789",
"status": "SUCCESS",
"total": 2,
"completed": 2,
"merge": true,
"mergedUrl": "https://pdforge-production.s3.us-east-2.amazonaws.com/merged/...",
"results": [...]
}
```
## Parameters
An array of PDF generation items. Minimum 1 item, maximum 20 items per batch.
Each item accepts the following properties:
The id of your PDF template
The object containing the variables from your PDF template
If you're using our embedded white-label solution, you can pass the UUID of
your customer to generate the PDF of the customized template
If true, will return a .PNG file instead of a .PDF file
The id of the active s3 connection you want to store your generated file on.
(only available in the high plan)
The path, including subdirectories and the filename without extension, to use
when saving the render in your S3 bucket. (only available if being stored in
custom s3\_bucket)
This object containing the metadata for your PDF. [See all the options
here](/api-reference/options/pdf-metadata).
If true, will hide the footer and the header elements on the first page of the
generated PDF
If true, will enter Debug Mode and show additional properties on the response
to help you figure out your variables and html rendered content. [Here's
everything you need to know about Debug
Mode](/api-reference/options/debug-mode)
If true, the request will return immediately with a `batchRequestId` and
`statusUrl`. You can poll the [Get Batch PDF Status](/api-reference/pdf-status/get-batch)
endpoint to track progress.
If true, all successfully generated PDFs will be merged into a single file.
The merged file URL will be available in the `mergedUrl` field of the response.
Cannot be used when any item has `convertToImage` set to `true`.
A URL to receive a POST request when the batch is complete. The webhook payload
will contain the full batch status and results.
Number of seconds that the generated signed URLs will take to expire (default:
1 hour)
## Async Mode
When `async` is set to `true`, the request returns immediately with a `batchRequestId` and a `statusUrl`:
```json theme={null}
{
"batchRequestId": "pdfnoodle_batch_123456789",
"statusUrl": "https://api.pdfnoodle.com/v1/pdf/batch/status/pdfnoodle_batch_123456789",
"total": 2,
"subRequestIds": ["pdfnoodle_request_abc123", "pdfnoodle_request_def456"],
"message": "Batch PDF generation started. Check the status URL for progress."
}
```
You can then check the status of your batch using the [Get Batch PDF Status](/api-reference/pdf-status/get-batch) endpoint.
## Payload Size Limit
The request body is limited to **3MB**. Since batch requests contain multiple items, this limit can be reached when sending many items with large `data` objects.
If your request exceeds this limit, you'll receive a `413` status code with the following error:
```json theme={null}
{
"message": "request entity too large"
}
```
If you're hitting the 3MB payload limit, consider splitting your batch into
multiple smaller requests. For example, instead of sending 20 items in a single
request, send two requests with 10 items each.
## Checking Batch Status
You can check the status of your batch at any time using the [Get Batch PDF Status](/api-reference/pdf-status/get-batch) endpoint with the `batchRequestId`.
To check the status of an individual PDF within the batch, use the [Get PDF Status](/api-reference/pdf-status/get) endpoint with the individual `requestId` from the results.
# Synchronous request
Source: https://docs.pdfnoodle.com/api-reference/generate-pdf-from-template/synchronous
POST /v1/pdf/sync
Synchronous means that when calling the API the request will wait for the pdf file to be generated before returning a response.
We recommend using asynchronous requests for better performance on your
application.
POST [https://api.pdfnoodle.com/v1/pdf/sync](https://api.pdfnoodle.com/v1/pdf/sync)
## Request
```bash cURL theme={null}
curl --location 'https://api.pdfnoodle.com/v1/pdf/sync' \
--header 'Authorization: Bearer pdfnoodle_api_123456789' \
--header 'Content-Type: application/json' \
--data '{
"templateId": "check",
"data": {
"currentDDate": "01/12/2025",
"user": {
"name": "John Doe",
"email": "john.doe@email.com"
},
"details": [{"score": "100", "description": "high score"}]
}
}'
```
```javascript JavaScript theme={null}
const response = await fetch("https://api.pdfnoodle.com/v1/pdf/sync", {
method: "POST",
headers: {
Authorization: "Bearer pdfnoodle_api_123456789",
"Content-Type": "application/json",
},
body: JSON.stringify({
templateId: "check",
data: {
currentDDate: "01/12/2025",
user: {
name: "John Doe",
email: "john.doe@email.com",
},
details: [{ score: "100", description: "high score" }],
},
}),
});
const result = await response.json();
```
```python Python theme={null}
import requests
response = requests.post(
'https://api.pdfnoodle.com/v1/pdf/sync',
headers={
'Authorization': 'Bearer pdfnoodle_api_123456789',
'Content-Type': 'application/json'
},
json={
'templateId': 'check',
'data': {
'currentDDate': '01/12/2025',
'user': {
'name': 'John Doe',
'email': 'john.doe@email.com'
},
'details': [{'score': '100', 'description': 'high score'}]
}
}
)
result = response.json()
```
## Response
```json Success (200 OK) theme={null}
{
"signedUrl": "https://pdforge-production.s3.us-east-2.amazonaws.com/...",
"metadata": {
"executionTime": "1.805 seconds",
"fileSize": "4.066 kB"
}
}
```
```json Timeout (202 Accepted) theme={null}
{
"requestId": "pdfnoodle_request_123456789",
"statusUrl": "https://api.pdfnoodle.com/v1/pdf/status/pdfnoodle_request_123456789",
"message": "Couldn't generate PDF within 30 seconds, a queue was added to generate it asynchronously. Check the queue status on this URL."
}
```
This endpoint responds with `200 OK` once the PDF has been generated. The response body will contain a `signedUrl` key which is a temporary URL pointing to the generated PDF file on our S3 bucket. If you passed a custom `s3_bucket`, it'll be stored there instead. This URL will expire after the time specified in `signedUrlExpiresIn` (default: 1 hour).
If your PDF takes more than 30 seconds to render, it will automatically be added to an asynchronous queue and you'll receive a `202 Accepted` response with a `requestId`. You can check the status using the [Get PDF Status](/api-reference/pdf-status/get) endpoint.
### PDF Render Metadata
We'll also bring some additional metadata from your PDF render with every response:
* **executionTime** - Time in seconds it took to generate your PDF
* **fileSize** - PDF size in kiloBytes
## Parameters
The id of your PDF template
The object containing the variables from your PDF template
If true, will return a .PNG file instead of a .PDF file
This object containing the metadata for your PDF. [See all the options
here](/api-reference/options/pdf-metadata).
The id of the active s3 connection you want to store your generated file on.
(only available in the high plan)
The path, including subdirectories and the filename without extension, to use
when saving the render in your S3 bucket. (only available if being stored in
custom s3\_bucket)
Number of seconds that the generated signed URL will take to expire (default:
1 hour)
If you're using our embedded white-label solution, you can pass the UUID of
your customer to generate the PDF of the customized template
If true, will hide the footer and the header elements on the first page of the
generated PDF
If true, will enter Debug Mode and show additional properties on the response
to help you figure out your variables and html rendered content. [Here's
everything you need to know about Debug
Mode](/api-reference/options/debug-mode)
## Request Timeout (>30 seconds)
Synchronous requests are useful when you want to get the results back
immediately. However, if your PDF takes more than 30 seconds to render, it
will automatically be added to an asynchronous queue.
Sometimes, your PDF might take more than 30 seconds to render, if it has a lot of heavy images/charts and a lot of pages being generated.
**If that's the case and your PDF takes more than 30 seconds to render synchronously**, we'll automatically add it to an asynchronous queue to render it.
You'll be able to check if the PDF is completed using the [Get PDF Status](/api-reference/pdf-status/get) endpoint with the requestId you just received (or just by making a GET request on the statusUrl).
If the PDF is still rendering, you'll get a response like this:
```json theme={null}
{
"requestId": "pdfnoodle_request_123456789",
"renderStatus": "ONGOING",
"signedUrl": "",
"metadata": {}
}
```
And **once the PDF is ready**, you'll receive a response like this:
```json theme={null}
{
"requestId": "pdfnoodle_request_123456789",
"renderStatus": "SUCCESS",
"signedUrl": "https://pdforge-production.s3.us-east-2.amazonaws.com/...",
"metadata": {
"executionTime": "1.805 seconds",
"fileSize": "4.066 kB"
}
}
```
# Authentication
Source: https://docs.pdfnoodle.com/api-reference/getting-started/authentication
To authenticate you need to add an *Authorization* header with the contents of the header being `Bearer pdfnoodle_api_123456789` where `pdforge_api_123456789` is your API Key.
Example:
```
Authorization: Bearer pdfnoodle_api_123456789
```
When you create your account, we'll automatically generate 2 API Keys for you, but you can create how many more you'd like. You can access them on the API Keys menu on the sidebar.
# Rate limit
Source: https://docs.pdfnoodle.com/api-reference/getting-started/rate-limit
Your rate limit for **synchronous requests** will depend on your plan.
| Plan | Sync Rate Limit |
| ------------ | ----------------------- |
| **Starter** | 60 requests per minute |
| **Business** | 240 requests per minute |
| **Scale** | 360 requests per minute |
**Asynchronous requests** have a higher rate limit of **1.000 request per minute for both plans**.
**Batch requests** (`/batch` endpoints) have a rate limit of **50 requests per minute for both plans**. Since each batch request can contain up to 20 items, this still allows generating up to 1.000 PDFs per minute.
After that, you'll hit the rate limit and receive a `429` response error code.
To prevent this, we recommend reducing the rate at which you request the API. This can be done by introducing a queue mechanism or reducing the number of concurrent requests per second. If you have specific requirements, [contact support](mailto:support@pdforge.com) to request a rate increase.
# Migration guide: from pdforge to pdf noodle
Source: https://docs.pdfnoodle.com/api-reference/guides/migration-guide-from-pdforge-to-pdf-noodle
# Migration guide: from pdforge to pdf noodle
Migrating from pdforge to pdf noodle is designed to be smooth and low-risk. This guide explains what is changing, what remains exactly the same, and the minimal steps you can take to align your stack with the new brand and domain without breaking any existing workflows.
Whether you are a developer, no-code builder, automation enthusiast, or SaaS founder, this article will help you migrate with confidence.
***
### 1. Introduction
pdf noodle is the new name and brand identity for what used to be pdforge. Under the hood, it is the same platform, the same infrastructure, and the same PDF engine you’re already using in production.
**A quick overview:**
* Your templates, variables, and payloads still work the same way
* Your API keys, auth, and rate limits remain unchanged
* Your integrations (n8n, Make, Zapier, Bubble, custom code) continue to run normally
* All pdforge URLs are redirected to the new domain and will keep working until the end of 2026
This guide explains what is changing at the branding and domain level and gives you a short, practical migration path you can follow at your own pace.
***
### 2. What’s Changing
Here’s what is actually new with the rebrand.
* **New brand name**
* From pdforge to pdf noodle
* **New domain**
* From pdforge.com to pdfnoodle.com
* **Updated documentation and dashboard UI**
* Logo, colors, and wording now reflect the PDF Noodle brand
* Navigation and features remain familiar
* **New endpoints** (optional but recommended)
* New base URL under api.pdfnoodle.com
* Old api.pdforge.com URLs are redirected and remain valid until end of 2026
Nothing in your payload structure, Handlebars variables, or template conventions changes with this rebrand.
***
### 3. What’s Not Changing
Under the hood, pdf noodle is the same engine you already trust. The rebrand does not affect:
* **API behavior**
* Same endpoints, same request/response shape, same error codes
* **Template schemas**
* Your Handlebars variables like `{{customer_name}}`, `{{{html_block}}}`, loops, conditions, and visibility logic stay exactly the same
* **Auth tokens and API keys**
* No need to regenerate keys or rotate credentials
* **PDF generation engine**
* Same reliability, performance, and rendering behavior
* **Integrations**
* n8n, Make, Zapier, Bubble, custom scripts keep working as they are
* **Existing pdforge URLs**
* All existing endpoints and dashboard URLs under pdforge.com will keep working until the end of 2026
You can migrate gradually, on your own timeline, without risking downtime or breaking existing PDF flows.
***
### 4. Recommended Migration Path
You don’t need to do everything at once. The recommended approach is:
1. **Update your API base URLs**
* Switch from api.pdforge.com to api.pdfnoodle.com in your code and integrations
2. **Test via Debug Mode**
* Use Debug Mode (where available) to inspect rendered HTML, variables, and template schemas when testing the new domain
3. **Update environment variables**
* Centralize your base URL and keep it configurable (for staging, production, etc.)
4. **Update webhooks and callback URLs** (optional but recommended)
* If you have callbacks pointing to pdforge.com, update them to pdfnoodle.com for long-term consistency
5. **Validate template rendering**
* Trigger example runs in staging or sandbox environments
* Make sure variables are being sent and resolved correctly
6. **Roll out to production**
* Once validated, deploy your changes and monitor as usual
**Tip:** If you already use environment variables for your base URL, this
migration can often be done with a single config change and a quick smoke
test.
***
### 5. Update Your API URLs
The most important technical change is the base URL. Paths and behavior remain identical.
**Old base URL:**
```
https://api.pdforge.com
```
**New base URL:**
```
https://api.pdfnoodle.com
```
What stays the same:
* All endpoint paths remain the same
* No changes to request bodies or response formats
* No changes to auth headers or token usage
* api.pdforge.com will remain active until the end of 2026
**Example: if you previously used:**
```bash theme={null}
curl --location 'https://api.pdforge.com/v1/pdf/sync' \
--header 'Authorization: Bearer pdforge_api_123456789' \
--header 'Content-Type: application/json' \
--data '{
"templateId": "check",
"data":{
"currentDDate": "01/12/2025",
"user": {
"name":"John Doe",
"email": "john.doe@email.com"
},
"details": [{"score":"100", "description":"high score"}]
}
}
'
```
**You can safely update to:**
```bash theme={null}
curl --location 'https://api.pdfnoodle.com/v1/pdf/sync' \
--header 'Authorization: Bearer pdforge_api_123456789' \
--header 'Content-Type: application/json' \
--data '{
"templateId": "check",
"data":{
"currentDDate": "01/12/2025",
"user": {
"name":"John Doe",
"email": "john.doe@email.com"
},
"details": [{"score":"100", "description":"high score"}]
}
}
'
```
The Handlebars variables convention (e.g. `{{customer_name}}`, `{{{html_block}}}`) remains exactly the same inside your templates.
***
### 6. Environment Variables
If you already use environment variables for configuration, migration becomes much easier and safer.
A typical setup might look like this:
```
PDF_API_URL=https://api.pdforge.com
PDF_API_KEY=your-existing-key
```
You can update it to:
```
PDF_API_URL=https://api.pdfnoodle.com
PDF_API_KEY=your-existing-key
```
* No API key changes are required
* You can switch back to the old URL at any time during testing if needed
* This pattern keeps your staging and production environments flexible
**Tip:** If you are dynamically sending variables as payload to pdf noodle
(for example, building a variables object from your own identifiers), nothing
changes. The “Send data dynamically” convention still applies exactly as
before.
***
### 8. n8n • Make.com • Zapier • Bubble Integrations
If you use pdf noodle (formerly pdforge) through no-code or automation platforms, here’s what you need to know.
#### n8n
* The current pdforge node / package will continue to work as is
* The underlying API will keep working through redirects until end of 2026
* At first, we will only change the branding and description to reflect pdf noodle
* In the future, we plan to release a new, dedicated pdf noodle package
* When we do that, the older pdforge package will be gradually deprecated
* You will be informed well in advance and given a clear migration path
#### Make.com
* Existing custom modules or HTTP integrations that point to api.pdforge.com will continue to work through redirects
* You can optionally update HTTP modules and custom integrations to use [https://api.pdfnoodle.com](https://api.pdfnoodle.com) to align with the new domain
#### Zapier
* If you use Zapier via webhooks or custom integrations that call api.pdforge.com, no immediate change is required
* You can update the URLs to api.pdfnoodle.com when convenient for consistency
#### Bubble and Other API Connectors
* If you use Bubble’s API Connector (or similar tools) with `https://api.pdforge.com` as the base URL:
* Update the base URL to `https://api.pdfnoodle.com`
* No changes are required to the request structure or JSON payload
Overall, all existing integrations keep working. The main change is branding and base URL alignment, which you can adopt progressively.
***
### 9. Frequently Asked Questions
#### Do I need a new API key?
No. Your existing API keys remain valid and continue to work for both pdforge.com and pdfnoodle.com domains.
#### Are my templates affected?
No. All your templates, Handlebars variables, visibility rules, and margins continue to work exactly as they did before.
#### Do redirects affect speed?
Redirects add a minimal overhead, but for most use cases this is negligible. For optimal performance and future-proofing, we recommend updating to `https://api.pdfnoodle.com` when convenient.
#### Will pdforge URLs stop working immediately?
No. All pdforge URLs are expected to remain functional through the end of 2026 via permanent 301 redirects. You have plenty of time to migrate gradually.
#### Do I need to update my plan or billing?
No. Your plan, pricing, and billing setup remain exactly the same. This is a brand and domain change only, not a pricing or product reset.
***
**If you have any questions about migration or run into any issues, feel free to reach out:**
**📩 `support@pdfnoodle.com`**
We’re here to help you switch over smoothly and keep your PDFs running flawlessly.
# Overview
Source: https://docs.pdfnoodle.com/api-reference/index
Welcome to PDF Noodle API Reference
# API Reference
Welcome to the PDF Noodle API Reference. Here you'll find everything you need to integrate PDF generation into your application.
## Getting Started
Learn how to authenticate your API requests
Understand API rate limits and usage
## PDF Generation
Generate PDFs using reusable templates
Convert HTML directly to PDF
## PDF Tools
Check the status of async tool operations
Get presigned URLs for uploading and downloading PDFs
Combine multiple PDF files into one document
Split a PDF into parts by ranges or intervals
Reduce PDF file size with configurable compression
Convert Markdown content into styled PDFs
Update title, author, and other PDF metadata
## Templates
Create new templates using AI based on text prompts
Retrieve all templates available for your company
Extract variable schema from templates
Retrieve complete template details and check creation status
# Debug Mode
Source: https://docs.pdfnoodle.com/api-reference/options/debug-mode
# Debug Mode
Debug Mode helps you quickly troubleshoot why a template didn’t render as expected, without guessing. Use it while building or debugging; turn it off for production runs. It only works on the PDF from templates endpoints.
### What you get with Debug Mode
When debug is enabled, the response includes:
* **Rendered HTML** – the final HTML string sent to the PDF engine.
* **Sent Variables** – the exact payload received by the template.
* **Template Schema** – variables the template expects (including each arrays).
* **Are Variables Valid**? – If the schema from sent variables equals template schema.
* **Missing Variables** – list of required variables not provided on the payload
* **Has Draft Version** – If there's a draft version of this template that has not been published yet (The API only has access to published versions)
**Performance note:** Debug Mode adds extra work and slows down the request. Use it only for investigation.
### How to activate Debug Mode
Just add `debug: true` to your request body.
**Example of request:**
```bash theme={null}
curl --location 'https://api.pdfnoodle.com/v1/pdf/sync' \
--header 'Authorization: Bearer pdfnoodle_api_123456789' \
--header 'Content-Type: application/json' \
--data '{
"templateId": "check",
"debug": true,
"data":{
"currentDDate": "01/12/2025",
"user": {
"name":"John Doe",
"email": "john.doe@email.com"
},
"details": [{"score":"100", "description":"high score"}]
}
}
'
```
On your response, you'll get an additional object with the debug information:
```json theme={null}
{
"signedUrl": "https://pdforge-production.s3.us-east-2.amazonaws.com/...",
"metadata": {
"executionTime": "1.805 seconds",
"fileSize": "4.066 kB"
},
"debug":{
"renderedHtml": "....",
"templateSchema": {...},
"sentVariables": {...},
"isVariablesValid": true,
"missingVariables": [...],
"hasDraftVersion": true
}
}
```
# How render PNG instead of PDF
Source: https://docs.pdfnoodle.com/api-reference/options/how-render-png-instead-of-pdf
# How render PNG instead of PDF
## How to do it
To generate the same layout but as a .PNG instead of a .PDF file, you can use the same endpoints `/v1/pdf/sync` and `/v1/pdf/async`, but passing an extra parameter `convertToImage:true` on the body payload.
If you want to generate the image synchronously, you can use this guide:
[Generate PDF from Template Synchronously](/api-reference/generate-pdf-from-template/synchronous)
Or if you want to generate the image asynchronously, you can use this guide:
[Generate PDF from Template Asynchronously](/api-reference/generate-pdf-from-template/asynchronous)
**Here's a request example for a synchronous render:**
```
curl --location 'https://api.pdfnoodle.com/v1/pdf/sync' \
--header 'Authorization: Bearer pdfnoodle_api_123456789' \
--header 'Content-Type: application/json' \
--data '{
"templateId": "check",
"convertToImage": true,
"data":{
"currentDDate": "01/12/2025",
"user": {
"name":"John Doe",
"email": "john.doe@email.com"
},
"details": [{"score":"100", "description":"high score"}]
}
}
'
```
The result will be the same **signedUrl** described on the guides, but with a .PNG file instead.
# PDF metadata
Source: https://docs.pdfnoodle.com/api-reference/options/pdf-metadata
# PDF metadata
### PDF Metadata options
Here's a list of all the **optional parameters** that you can send as an object "metadata" while using our endpoints:
A human-readable name for the document, like a book title. Default: the name
of the template
The individual or organization that created the document. Default: `pdfnoodle`
A brief description of the document's topic.
A list of keywords describing the content.
The original software that created the content before it was turned into a
PDF. Default: `pdfnoodle`
These values will appear in the PDF's file details. On macOS, you can check them by right-clicking the file and selecting **Get Info** in Finder.
The only field you can't override is the **Encoding software.** It's
automatically set by our system.
# PDF Params
Source: https://docs.pdfnoodle.com/api-reference/options/pdf-params
# PDF Params
**When using reusable templates**, these parameters are set on the interface,
so you don't need send any extra parameters on the API.
### PDF Params options
Here's a list of all the **optional parameters** that you can send as an object "pdfParams" while using our HTML to PDF endpoint:
Whether to show the header and footer. Default: `false`
HTML template for the header. [See valid HTML markup
bellow](#header-and-footer-valid-html-markups).
HTML template for the footer. [See valid HTML markup
bellow](#header-and-footer-valid-html-markups).
Set to `true` to print background graphics. Default: `false`
Whether to print in landscape orientation. Default: `false`
All the valid paper format types when printing a PDF. [See all format options
here](#paper-formats). Default: `Letter`
Paper width, accepts values labeled with units.
Paper height, accepts values labeled with units.
Object that sets the margin of every page. [See all margin options
here](#margin-options).
Give any CSS `@page` size declared in the page priority over what is declared
in the `width` or `height` or `format` option. Default: `false`
Whether or not to embed the document outline into the PDF. Default: `false`
The **width**, **height**, and **margin** options accept values labeled with units. Unlabeled values are treated as pixels.
All possible formats:
* `width`: 100 (prints with width set to 100 pixels)
* `width`: '100px' (prints with width set to 100 pixels)
* `width`: '100in' (prints with width set to 100 inches)
* `width`: '100cm,' (prints with width set to 100 centimeters)
* `width`: '100mm' (prints with width set to 100 millimeters)
***
### Header and Footer: Valid HTML Markups
HTML template for the print header. Should be valid HTML markup with following classes used to inject printing values into them:
* `'date'` formatted print date
* `'title'` document title
* `'url'` document location
* `'pageNumber'` current page number
* `'totalPages'` total pages in the document
**Example:**
```html theme={null}
Some custom footer text here
|
Page of
```
***
### Paper format options
| Format name | 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 |
***
### Margin Options
Top margin, accepts values labeled with units. Default: `0`
Right margin, accepts values labeled with units. Default: `0`
Left margin, accepts values labeled with units. Default: `0`
Bottom margin, accepts values labeled with units. Default: `0`
* Example:
```json theme={null}
{
"margin": {
"top": 10,
"right": "10px",
"bottom": "10in",
"left": "10cm"
}
}
```
# Storage Options (s3)
Source: https://docs.pdfnoodle.com/api-reference/options/storage-options-s3
# Storage Options (s3)
### Setting Up S3 Storage
You're on the **High plan** and want to store your files in a custom S3 bucket?
We’ve got a full guide on how to set up S3 connections and switch the default bucket directly from the dashboard. [**Check out the guide**](https://app.gitbook.com/s/0ZiE10OwIphqlXGMhEL6/guides/saving-to-your-s3-storage).
### Configuration Options
#### `s3_bucket`
Set this to the ID of an active S3 connection to override the default storage location.
#### `s3_key`
default: `pdforge/{`[`template_id`](#user-content-fn-1)[^1]`}/`[`{auto_generated_filename}`](#user-content-fn-2)[^2]
Define the path (folders and filename, without extension) where your file will be saved inside your S3 bucket.
**Don’t include the file extension** — we’ll handle that based on the file type (e.g., .pdf, .png).
**Here's a request example for a synchronous render:**
```
curl --location 'https://api.pdfnoodle.com/v1/pdf/sync' \
--header 'Authorization: Bearer pdfnoodle_api_123456789' \
--header 'Content-Type: application/json' \
--data '{
"templateId": "template_id",
"s3_bucket": "s3_123456789",
"s3_key": "path/to/your/file",
"data":{
"currentDDate": "01/12/2025",
"user": {
"name":"John Doe",
"email": "john.doe@email.com"
},
"details": [{"score":"100", "description":"high score"}]
}
}
'
```
[^1]: Your template id
[^2]: An auto generated random hash
# Get PDF Status
Source: https://docs.pdfnoodle.com/api-reference/pdf-status/get
GET /v1/pdf/status/:requestId
Retrieve the status of a PDF generation request using the requestId returned from asynchronous requests or when synchronous requests timeout.
Use this endpoint to check the status of a single PDF generation request. This is particularly useful when:
You've made an asynchronous request and want to poll for status instead of waiting for the webhook
A synchronous request timed out (>30 seconds) and was automatically queued asynchronously
If you're looking to check the status of a batch PDF generation, use the [Get Batch PDF Status](/api-reference/pdf-status/get-batch) endpoint instead.
GET [https://api.pdfnoodle.com/v1/pdf/status/:requestId](https://api.pdfnoodle.com/v1/pdf/status/:requestId)
## Request
```bash cURL theme={null}
curl --location 'https://api.pdfnoodle.com/v1/pdf/status/pdfnoodle_request_123456789' \
--header 'Authorization: Bearer pdfnoodle_api_123456789'
```
```javascript JavaScript theme={null}
const response = await fetch(
"https://api.pdfnoodle.com/v1/pdf/status/pdfnoodle_request_123456789",
{
method: "GET",
headers: {
Authorization: "Bearer pdfnoodle_api_123456789",
},
}
);
const result = await response.json();
```
```python Python theme={null}
import requests
response = requests.get(
'https://api.pdfnoodle.com/v1/pdf/status/pdfnoodle_request_123456789',
headers={
'Authorization': 'Bearer pdfnoodle_api_123456789'
}
)
result = response.json()
```
## Response
The endpoint responds with `200 OK` and returns the current status of the PDF generation:
```json ONGOING theme={null}
{
"requestId": "pdfnoodle_request_123456789",
"renderStatus": "ONGOING",
"signedUrl": "",
"metadata": {}
}
```
```json SUCCESS theme={null}
{
"requestId": "pdfnoodle_request_123456789",
"renderStatus": "SUCCESS",
"signedUrl": "https://pdforge-production.s3.us-east-2.amazonaws.com/...",
"metadata": {
"executionTime": "1.805 seconds",
"fileSize": "4.066 kB"
}
}
```
```json FAILED theme={null}
{
"requestId": "pdfnoodle_request_123456789",
"renderStatus": "FAILED",
"signedUrl": "",
"metadata": {}
}
```
## Parameters
The requestId returned from an asynchronous request or from a synchronous
request that timed out
## Response Fields
The unique identifier for this PDF generation request
The current status of the PDF generation. Possible values: - `ONGOING` - The
PDF is still being generated - `SUCCESS` - The PDF has been successfully
generated - `FAILED` - The PDF generation failed
A temporary URL pointing to the generated PDF file. This will be empty (`""`)
when `renderStatus` is `ONGOING` or `FAILED`. The URL expires after 1 hour.
Additional metadata about the PDF render. Contains: - **executionTime** - Time
in seconds it took to generate your PDF (only present when `renderStatus` is
`SUCCESS`) - **fileSize** - PDF size in kiloBytes (only present when
`renderStatus` is `SUCCESS`)
If the `renderStatus` is `FAILED`, you should contact support to figure out
why your PDF generation failed.
## Usage Example
Here's a complete example of polling for PDF status:
```javascript theme={null}
async function checkPdfStatus(requestId) {
const maxAttempts = 60; // Poll for up to 5 minutes (60 * 5 seconds)
let attempts = 0;
while (attempts < maxAttempts) {
const response = await fetch(
`https://api.pdfnoodle.com/v1/pdf/status/${requestId}`,
{
method: "GET",
headers: {
Authorization: "Bearer pdfnoodle_api_123456789",
},
}
);
const result = await response.json();
if (result.renderStatus === "SUCCESS") {
return result.signedUrl;
}
if (result.renderStatus === "FAILED") {
throw new Error("PDF generation failed");
}
// Wait 5 seconds before checking again
await new Promise((resolve) => setTimeout(resolve, 5000));
attempts++;
}
throw new Error("PDF generation timed out");
}
```
```python theme={null}
import requests
import time
def check_pdf_status(request_id):
max_attempts = 60 # Poll for up to 5 minutes (60 * 5 seconds)
attempts = 0
while attempts < max_attempts:
response = requests.get(
f'https://api.pdfnoodle.com/v1/pdf/status/{request_id}',
headers={
'Authorization': 'Bearer pdfnoodle_api_123456789'
}
)
result = response.json()
if result['renderStatus'] == 'SUCCESS':
return result['signedUrl']
if result['renderStatus'] == 'FAILED':
raise Exception('PDF generation failed')
# Wait 5 seconds before checking again
time.sleep(5)
attempts += 1
raise Exception('PDF generation timed out')
```
# Get Batch PDF Status
Source: https://docs.pdfnoodle.com/api-reference/pdf-status/get-batch
GET /v1/pdf/batch/status/:batchRequestId
Retrieve the status of a batch PDF generation request using the batchRequestId.
Use this endpoint to check the status of a batch PDF generation request. This is useful when:
You've sent a batch request with `async: true` and want to poll for progress
A synchronous batch request timed out (>30 seconds) and you need to check on its progress
If you want to check the status of a single PDF generation (not a batch), use the [Get PDF Status](/api-reference/pdf-status/get) endpoint instead.
GET [https://api.pdfnoodle.com/v1/pdf/batch/status/:batchRequestId](https://api.pdfnoodle.com/v1/pdf/batch/status/:batchRequestId)
## Request
```bash cURL theme={null}
curl --location 'https://api.pdfnoodle.com/v1/pdf/batch/status/pdfnoodle_batch_123456789' \
--header 'Authorization: Bearer pdfnoodle_api_123456789'
```
```javascript JavaScript theme={null}
const response = await fetch(
"https://api.pdfnoodle.com/v1/pdf/batch/status/pdfnoodle_batch_123456789",
{
method: "GET",
headers: {
Authorization: "Bearer pdfnoodle_api_123456789",
},
}
);
const result = await response.json();
```
```python Python theme={null}
import requests
response = requests.get(
'https://api.pdfnoodle.com/v1/pdf/batch/status/pdfnoodle_batch_123456789',
headers={
'Authorization': 'Bearer pdfnoodle_api_123456789'
}
)
result = response.json()
```
## Response
The endpoint responds with `200 OK` and returns the current status of the batch PDF generation:
```json ONGOING theme={null}
{
"batchRequestId": "pdfnoodle_batch_123456789",
"status": "ONGOING",
"total": 3,
"completed": 1,
"merge": false,
"results": [
{
"requestId": "pdfnoodle_request_abc123",
"renderStatus": "SUCCESS",
"signedUrl": "https://pdforge-production.s3.us-east-2.amazonaws.com/...",
"metadata": {
"executionTime": "1.805 seconds",
"fileSize": "4.066 kB"
}
},
{
"requestId": "pdfnoodle_request_def456",
"renderStatus": "ONGOING",
"signedUrl": "",
"metadata": {}
},
{
"requestId": "pdfnoodle_request_ghi789",
"renderStatus": "ONGOING",
"signedUrl": "",
"metadata": {}
}
]
}
```
```json SUCCESS theme={null}
{
"batchRequestId": "pdfnoodle_batch_123456789",
"status": "SUCCESS",
"total": 2,
"completed": 2,
"merge": false,
"results": [
{
"requestId": "pdfnoodle_request_abc123",
"renderStatus": "SUCCESS",
"signedUrl": "https://pdforge-production.s3.us-east-2.amazonaws.com/...",
"metadata": {
"executionTime": "1.805 seconds",
"fileSize": "4.066 kB"
}
},
{
"requestId": "pdfnoodle_request_def456",
"renderStatus": "SUCCESS",
"signedUrl": "https://pdforge-production.s3.us-east-2.amazonaws.com/...",
"metadata": {
"executionTime": "2.103 seconds",
"fileSize": "5.200 kB"
}
}
]
}
```
```json PARTIAL_SUCCESS theme={null}
{
"batchRequestId": "pdfnoodle_batch_123456789",
"status": "PARTIAL_SUCCESS",
"total": 2,
"completed": 2,
"merge": false,
"results": [
{
"requestId": "pdfnoodle_request_abc123",
"renderStatus": "SUCCESS",
"signedUrl": "https://pdforge-production.s3.us-east-2.amazonaws.com/...",
"metadata": {
"executionTime": "1.805 seconds",
"fileSize": "4.066 kB"
}
},
{
"requestId": "pdfnoodle_request_def456",
"renderStatus": "FAILED",
"signedUrl": "",
"metadata": {}
}
]
}
```
```json FAILED theme={null}
{
"batchRequestId": "pdfnoodle_batch_123456789",
"status": "FAILED",
"total": 2,
"completed": 2,
"merge": false,
"results": [
{
"requestId": "pdfnoodle_request_abc123",
"renderStatus": "FAILED",
"signedUrl": "",
"metadata": {}
},
{
"requestId": "pdfnoodle_request_def456",
"renderStatus": "FAILED",
"signedUrl": "",
"metadata": {}
}
]
}
```
## Parameters
The batchRequestId returned from a batch PDF generation request
## Response Fields
The unique identifier for this batch PDF generation request
The current status of the batch. Possible values:
* `ONGOING` - PDFs are still being generated
* `SUCCESS` - All PDFs have been successfully generated
* `PARTIAL_SUCCESS` - Some PDFs were generated, but at least one failed
* `FAILED` - All PDFs failed to generate
The total number of PDFs in the batch
The number of PDFs that have finished processing (either succeeded or failed)
Whether the batch was requested with merge enabled
A temporary URL pointing to the merged PDF file. Only present when `merge` is `true`
and at least one PDF was generated successfully. The URL expires after the time
specified in `signedUrlExpiresIn`.
An array of individual PDF results. Each result contains:
* **requestId** - Unique identifier for the individual PDF request
* **renderStatus** - `ONGOING`, `SUCCESS`, or `FAILED`
* **signedUrl** - Temporary URL to the generated PDF (empty when `ONGOING` or `FAILED`)
* **metadata** - Contains `executionTime` and `fileSize` on `SUCCESS`
If any individual PDF has `renderStatus` of `FAILED`, you should contact support
to figure out why the PDF generation failed.
## Usage Example
Here's a complete example of polling for batch PDF status:
```javascript theme={null}
async function checkBatchStatus(batchRequestId) {
const maxAttempts = 60; // Poll for up to 5 minutes (60 * 5 seconds)
let attempts = 0;
while (attempts < maxAttempts) {
const response = await fetch(
`https://api.pdfnoodle.com/v1/pdf/batch/status/${batchRequestId}`,
{
method: "GET",
headers: {
Authorization: "Bearer pdfnoodle_api_123456789",
},
}
);
const result = await response.json();
if (result.status === "SUCCESS" || result.status === "PARTIAL_SUCCESS") {
return result;
}
if (result.status === "FAILED") {
throw new Error("All PDFs in the batch failed to generate");
}
// Wait 5 seconds before checking again
await new Promise((resolve) => setTimeout(resolve, 5000));
attempts++;
}
throw new Error("Batch PDF generation timed out");
}
```
```python theme={null}
import requests
import time
def check_batch_status(batch_request_id):
max_attempts = 60 # Poll for up to 5 minutes (60 * 5 seconds)
attempts = 0
while attempts < max_attempts:
response = requests.get(
f'https://api.pdfnoodle.com/v1/pdf/batch/status/{batch_request_id}',
headers={
'Authorization': 'Bearer pdfnoodle_api_123456789'
}
)
result = response.json()
if result['status'] in ['SUCCESS', 'PARTIAL_SUCCESS']:
return result
if result['status'] == 'FAILED':
raise Exception('All PDFs in the batch failed to generate')
# Wait 5 seconds before checking again
time.sleep(5)
attempts += 1
raise Exception('Batch PDF generation timed out')
```
## Checking Individual PDF Status
If you want to check on the status of a specific PDF within the batch, you can use the [Get PDF Status](/api-reference/pdf-status/get) endpoint with the individual `requestId` from the `results` array.
# Create Template with AI
Source: https://docs.pdfnoodle.com/api-reference/templates/create-template
POST /v1/integration/templates/create
Creates a new template using AI based on a text prompt and optional file reference. This is an asynchronous operation that typically takes a couple of minutes to complete.
Template creation is processed asynchronously. The endpoint returns
immediately with a `templateId` that you can use to check the creation status.
Template creation usually takes a couple of minutes, but can vary based on
complexity and system load.
POST [https://api.pdfnoodle.com/v1/integration/templates/create](https://api.pdfnoodle.com/v1/integration/templates/create)
## Request
```bash cURL theme={null}
curl --location 'https://api.pdfnoodle.com/v1/integration/templates/create' \
--header 'Authorization: Bearer pdfnoodle_api_123456789' \
--header 'Content-Type: application/json' \
--data '{
"prompt": "Create an invoice template with company logo, billing address, line items table, and total amount",
"displayName": "Standard Invoice",
"fileUrl": "https://example.com/reference-invoice.pdf"
}'
```
```javascript JavaScript theme={null}
const response = await fetch(
"https://api.pdfnoodle.com/v1/integration/templates/create",
{
method: "POST",
headers: {
Authorization: "Bearer pdfnoodle_api_123456789",
"Content-Type": "application/json",
},
body: JSON.stringify({
prompt:
"Create an invoice template with company logo, billing address, line items table, and total amount",
displayName: "Standard Invoice",
fileUrl: "https://example.com/reference-invoice.pdf",
}),
}
);
const result = await response.json();
```
```python Python theme={null}
import requests
response = requests.post(
'https://api.pdfnoodle.com/v1/integration/templates/create',
headers={
'Authorization': 'Bearer pdfnoodle_api_123456789',
'Content-Type': 'application/json'
},
json={
'prompt': 'Create an invoice template with company logo, billing address, line items table, and total amount',
'displayName': 'Standard Invoice',
'fileUrl': 'https://example.com/reference-invoice.pdf'
}
)
result = response.json()
```
## Response
```json Success (202 Accepted) theme={null}
{
"message": "Async template creation started. It usually takes a couple of minutes to finish creation. You can check the status of the template creation by fetching it using the templateId.",
"fetchTemplateUrl": "https://api.pdfnoodle.com/v1/integration/templates/a1b2c3d4e5",
"templateId": "a1b2c3d4e5"
}
```
This endpoint responds with `202 Accepted` immediately after starting the template creation process. The response includes a `templateId` that you can use to check the creation status.
## Parameters
Text description of the template you want to create. This will be enriched by
AI to generate the final template. Provide detailed, specific prompts for
better results. Include information about: - Document type (invoice, receipt,
report, etc.) - Required sections (header, footer, body content) - Layout
preferences - Data fields that need to be included
Human-readable name for the template (e.g., "Invoice Template", "Receipt
Template")
Optional URL to a reference file (PDF, image, etc.) that the AI can use as
inspiration for the template design. This can help the AI understand your
design preferences if you have an existing document to reference.
## Response Fields
Informational message about the async template creation process
URL endpoint you can use to check the status of the template creation. Replace
`{templateId}` with the actual `templateId` value.
A randomly generated 10-character hexadecimal string that uniquely identifies
the template within your company context. Use this value to check the creation
status and fetch the template once it's ready.
## Checking Template Creation Status
After creating a template, you can check its status by polling the [Get Template](/api-reference/templates/get-template) endpoint using the `templateId` returned from this request.
The template will have different status values:
* **`ONGOING`**: Template creation is still in progress
* **`SUCCESS`**: Template has been created successfully
* **`FAILED`**: Template creation failed (check `metadata.errorMessage`)
## Usage Example
Here's a complete example of creating a template and polling for completion:
```javascript theme={null}
async function createTemplate(prompt, displayName, fileUrl) {
// 1. Create template
const createResponse = await fetch(
"https://api.pdfnoodle.com/v1/integration/templates/create",
{
method: "POST",
headers: {
Authorization: "Bearer pdfnoodle_api_123456789",
"Content-Type": "application/json",
},
body: JSON.stringify({
prompt,
displayName,
fileUrl,
}),
}
);
const { templateId, fetchTemplateUrl } = await createResponse.json();
// 2. Poll for completion
let templateReady = false;
while (!templateReady) {
await new Promise((resolve) => setTimeout(resolve, 15000)); // Wait 15 seconds
const statusResponse = await fetch(
`https://api.pdfnoodle.com/v1/integration/templates/${templateId}`,
{
headers: { Authorization: "Bearer pdfnoodle_api_123456789" },
}
);
const data = await statusResponse.json();
if (data.template) {
// Template is complete
templateReady = true;
console.log("Template ready:", data.template);
return data.template;
} else if (data.status === "SUCCESS") {
// Template creation completed
templateReady = true;
console.log("Template created:", data.templateHtml);
return data;
} else if (data.status === "FAILED") {
// Template creation failed
throw new Error(data.metadata.errorMessage);
}
// Otherwise, status is 'ONGOING', continue polling
}
}
// Usage
createTemplate(
"Create an invoice template with company logo, billing address, line items table, and total amount",
"Standard Invoice"
);
```
```python theme={null}
import requests
import time
def create_template(prompt, display_name, file_url=None):
# 1. Create template
response = requests.post(
'https://api.pdfnoodle.com/v1/integration/templates/create',
headers={
'Authorization': 'Bearer pdfnoodle_api_123456789',
'Content-Type': 'application/json'
},
json={
'prompt': prompt,
'displayName': display_name,
'fileUrl': file_url
}
)
result = response.json()
template_id = result['templateId']
# 2. Poll for completion
template_ready = False
while not template_ready:
time.sleep(15) # Wait 15 seconds
status_response = requests.get(
f'https://api.pdfnoodle.com/v1/integration/templates/{template_id}',
headers={'Authorization': 'Bearer pdfnoodle_api_123456789'}
)
data = status_response.json()
if 'template' in data:
# Template is complete
template_ready = True
print('Template ready:', data['template'])
return data['template']
elif data.get('status') == 'SUCCESS':
# Template creation completed
template_ready = True
print('Template created:', data['templateHtml'])
return data
elif data.get('status') == 'FAILED':
# Template creation failed
raise Exception(data['metadata']['errorMessage'])
# Otherwise, status is 'ONGOING', continue polling
# Usage
create_template(
'Create an invoice template with company logo, billing address, line items table, and total amount',
'Standard Invoice'
)
```
If template creation fails, the status will be set to `FAILED` and error
details will be available in the metadata when fetching the template. You
should handle these errors appropriately in your application.
## Error Responses
### 401 Unauthorized
```json theme={null}
{
"message": "Unauthorized"
}
```
**Occurs when:** The API key is missing or invalid.
### 500 Internal Server Error
```json theme={null}
{
"message": "Error message describing what went wrong"
}
```
**Occurs when:** An internal server error prevents the template creation from starting.
# Get Template
Source: https://docs.pdfnoodle.com/api-reference/templates/get-template
GET /v1/integration/templates/:templateId
Retrieves the complete template details, including its structure, components, and metadata. This endpoint can also be used to check the status of a template that is currently being created.
This endpoint returns different response structures depending on whether the
template is fully created or still being processed. Use this endpoint to poll
for completion when creating templates with AI.
GET [https://api.pdfnoodle.com/v1/integration/templates/:templateId](https://api.pdfnoodle.com/v1/integration/templates/:templateId)
## Request
```bash cURL theme={null}
curl --location 'https://api.pdfnoodle.com/v1/integration/templates/invoice-template-001' \
--header 'Authorization: Bearer pdfnoodle_api_123456789'
```
```javascript JavaScript theme={null}
const response = await fetch(
"https://api.pdfnoodle.com/v1/integration/templates/invoice-template-001",
{
method: "GET",
headers: {
Authorization: "Bearer pdfnoodle_api_123456789",
},
}
);
const result = await response.json();
```
```python Python theme={null}
import requests
response = requests.get(
'https://api.pdfnoodle.com/v1/integration/templates/invoice-template-001',
headers={
'Authorization': 'Bearer pdfnoodle_api_123456789'
}
)
result = response.json()
```
## Response
This endpoint returns different response structures depending on the template's status:
### Completed Template Response
```json Success - Completed Template (200 OK) theme={null}
{
"template": {
"id": "invoice-template-001",
"displayName": "Standard Invoice",
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z",
"style": {
"header": { "showHeader": true, "headerText": "..." },
"footer": { "showFooter": true, "footerText": "..." }
},
"type": "HTML",
"html": "..."
}
}
```
### Template Creation Status Response
```json ONGOING - Template Being Created (200 OK) theme={null}
{
"status": "ONGOING",
"templateId": "a1b2c3d4e5",
"templateHtml": "",
"metadata": {}
}
```
```json SUCCESS - Template Creation Completed (200 OK) theme={null}
{
"status": "SUCCESS",
"templateId": "a1b2c3d4e5",
"templateHtml": "...",
"pdfPreviewUrl": "https://s3.amazonaws.com/...",
"metadata": {
"timeToCreateTemplate": "45.2 seconds"
}
}
```
```json FAILED - Template Creation Failed (200 OK) theme={null}
{
"status": "FAILED",
"templateId": "a1b2c3d4e5",
"templateHtml": "",
"metadata": {
"errorMessage": "Error message describing what went wrong"
}
}
```
## Path Parameters
Template identifier. This can be: - The `name` field from an existing template
(same as `id` from list templates response) - The `templateId` returned from
the create template endpoint
## Response Fields
### Completed Template Response
Complete template object with all database fields
Template identifier (used in other endpoints)
Human-readable template name
Template creation timestamp (ISO 8601 format)
Template last update timestamp (ISO 8601 format)
Template styling configuration (header, footer, etc.)
Template type (e.g., "HTML", "NO\_CODE")
The HTML template content, including Handlebars variables for dynamic content
### Status Response (for templates being created)
Status of template creation. Possible values: - `ONGOING` - Template creation
is still in progress - `SUCCESS` - Template has been created successfully -
`FAILED` - Template creation failed
Template identifier
HTML template content. Empty if `ONGOING`, populated if `SUCCESS`
A signed URL to a PDF preview of the generated template, rendered with sample
data. The URL is valid for 7 days. Empty string if preview generation failed.
Only present when `status` is `SUCCESS`.
Additional metadata about the template creation
Time taken to create template (only present if `status` is `SUCCESS`)
Error message describing what went wrong (only present if `status` is
`FAILED`)
## Status Checking
When creating a template with AI, use this endpoint to poll for completion:
* **`ONGOING`**: Template creation is still in progress. Continue polling.
* **`SUCCESS`**: Template has been created successfully. The `templateHtml` field contains the generated template.
* **`FAILED`**: Template creation failed. Check `metadata.errorMessage` for details.
Status information for templates being created is cached in Redis for 24
hours. Once a template is fully created, the response format changes to the
full template object.
## Polling Strategy
When polling for template creation status:
* Poll every 10-30 seconds after initial creation
* Stop polling once `status` is `SUCCESS` or `FAILED`
* For completed templates, the response format changes to the full template object
* If a template doesn't exist and isn't in the creation queue, you'll receive a `404 Not Found` error
## Usage Example
Here's a complete example of checking template status:
```javascript theme={null}
async function checkTemplateStatus(templateId) {
const response = await fetch(
`https://api.pdfnoodle.com/v1/integration/templates/${templateId}`,
{
headers: { Authorization: "Bearer pdfnoodle_api_123456789" },
}
);
const data = await response.json();
// Check if template is complete
if (data.template) {
console.log("Template is complete:", data.template);
return data.template;
}
// Check creation status
if (data.status === "SUCCESS") {
console.log("Template created successfully!");
console.log("Time to create:", data.metadata.timeToCreateTemplate);
console.log("Template HTML:", data.templateHtml);
console.log("PDF Preview:", data.pdfPreviewUrl);
return data;
} else if (data.status === "FAILED") {
throw new Error(`Template creation failed: ${data.metadata.errorMessage}`);
} else if (data.status === "ONGOING") {
console.log("Template creation is still in progress...");
return null; // Continue polling
}
}
// Polling example
async function pollTemplateStatus(templateId) {
const maxAttempts = 40; // Poll for up to 10 minutes (40 * 15 seconds)
let attempts = 0;
while (attempts < maxAttempts) {
const result = await checkTemplateStatus(templateId);
if (result) {
return result; // Template is ready or failed
}
// Wait 15 seconds before checking again
await new Promise((resolve) => setTimeout(resolve, 15000));
attempts++;
}
throw new Error("Template creation timed out");
}
```
```python theme={null}
import requests
import time
def check_template_status(template_id):
response = requests.get(
f'https://api.pdfnoodle.com/v1/integration/templates/{template_id}',
headers={'Authorization': 'Bearer pdfnoodle_api_123456789'}
)
data = response.json()
# Check if template is complete
if 'template' in data:
print('Template is complete:', data['template'])
return data['template']
# Check creation status
if data.get('status') == 'SUCCESS':
print('Template created successfully!')
print('Time to create:', data['metadata']['timeToCreateTemplate'])
print('Template HTML:', data['templateHtml'])
print('PDF Preview:', data['pdfPreviewUrl'])
return data
elif data.get('status') == 'FAILED':
raise Exception(f"Template creation failed: {data['metadata']['errorMessage']}")
elif data.get('status') == 'ONGOING':
print('Template creation is still in progress...')
return None # Continue polling
# Polling example
def poll_template_status(template_id):
max_attempts = 40 # Poll for up to 10 minutes (40 * 15 seconds)
attempts = 0
while attempts < max_attempts:
result = check_template_status(template_id)
if result:
return result # Template is ready or failed
# Wait 15 seconds before checking again
time.sleep(15)
attempts += 1
raise Exception('Template creation timed out')
```
## Error Responses
### 401 Unauthorized
```json theme={null}
{
"message": "Unauthorized"
}
```
**Occurs when:** The API key is missing or invalid.
### 404 Not Found
```json theme={null}
{
"message": "Couldnt find this template."
}
```
**Occurs when:** The requested template doesn't exist and isn't in the creation queue.
### 500 Internal Server Error
```json theme={null}
{
"message": "Error message describing what went wrong"
}
```
**Occurs when:** An internal server error prevents the operation from completing.
If the `status` is `FAILED`, check the `metadata.errorMessage` field for
details about what went wrong. You may need to contact support if the error
persists.
# Get Template Variables
Source: https://docs.pdfnoodle.com/api-reference/templates/get-template-variables
GET /v1/integration/templates/:templateId/variables
Retrieves the variable schema for a specific template. This endpoint extracts all variables (data placeholders) that can be used when rendering the template.
GET [https://api.pdfnoodle.com/v1/integration/templates/:templateId/variables](https://api.pdfnoodle.com/v1/integration/templates/:templateId/variables)
## Request
```bash cURL theme={null}
curl --location 'https://api.pdfnoodle.com/v1/integration/templates/invoice-template-001/variables' \
--header 'Authorization: Bearer pdfnoodle_api_123456789'
```
```javascript JavaScript theme={null}
const response = await fetch(
"https://api.pdfnoodle.com/v1/integration/templates/invoice-template-001/variables",
{
method: "GET",
headers: {
Authorization: "Bearer pdfnoodle_api_123456789",
},
}
);
const result = await response.json();
```
```python Python theme={null}
import requests
response = requests.get(
'https://api.pdfnoodle.com/v1/integration/templates/invoice-template-001/variables',
headers={
'Authorization': 'Bearer pdfnoodle_api_123456789'
}
)
result = response.json()
```
## Response
```json Success (200 OK) theme={null}
{
"invoice_number": "your invoice_number here",
"company_info": {
"name": "your company_info.name here",
"address_line_1": "your company_info.address_line_1 here",
"city": "your company_info.city here",
"email": "your company_info.email here"
},
"billing_to": {
"name": "your billing_to.name here",
"address_line_1": "your billing_to.address_line_1 here",
"city": "your billing_to.city here"
},
"total_value": "your total_value here",
"items": [
{
"description": "your row.description here",
"quantity": "your row.quantity here",
"price": "your row.price here"
}
]
}
```
This endpoint responds with `200 OK` and returns all variables from the template. The response structure matches the variables used in the template, including nested objects and arrays.
The example above shows a variable schema from an invoice template. The actual
response will vary depending on the template's structure and variables.
## Path Parameters
Template identifier (the `name` field from the template, same as `id` from
list templates response, or the `templateId` returned from create template
endpoint)
## Response Fields
The response contains all variables from the template. The structure will vary depending on the template, but will include all variables, nested objects, and arrays that are used in the template.
## Usage
This endpoint is essential for:
* Understanding what data fields are required to render a template
* Building dynamic forms for template data input
* Validating data before rendering
* Generating API documentation for template-specific data structures
* Discovering available variables programmatically
The response structure matches the variables used in the template. Use this
endpoint to discover what data fields are required when rendering a template
with the generate-pdf-from-template endpoint.
## Usage Example
Here's a complete example of fetching template variables and using them to build a data payload:
```javascript theme={null}
// Get variables for a specific template
const templateId = "invoice-template-001";
const variablesResponse = await fetch(
`https://api.pdfnoodle.com/v1/integration/templates/${templateId}/variables`,
{
headers: { Authorization: "Bearer pdfnoodle_api_123456789" },
}
);
const { variables } = await variablesResponse.json();
console.log("Required variables:", variables);
// Use variables to build data payload for rendering
const templateData = {
companyName: "Acme Corp",
invoiceNumber: "INV-001",
lineItems: [
{
description: "Product A",
quantity: 2,
price: 50.0,
},
{
description: "Product B",
quantity: 1,
price: 30.0,
},
],
totalAmount: 130.0,
};
// Now you can use templateData with the generate-pdf-from-template endpoint
```
```python theme={null}
import requests
# Get variables for a specific template
template_id = 'invoice-template-001'
variables_response = requests.get(
f'https://api.pdfnoodle.com/v1/integration/templates/{template_id}/variables',
headers={'Authorization': 'Bearer pdfnoodle_api_123456789'}
)
result = variables_response.json()
variables = result['variables']
print('Required variables:', variables)
# Use variables to build data payload for rendering
template_data = {
'companyName': 'Acme Corp',
'invoiceNumber': 'INV-001',
'lineItems': [
{
'description': 'Product A',
'quantity': 2,
'price': 50.0
},
{
'description': 'Product B',
'quantity': 1,
'price': 30.0
}
],
'totalAmount': 130.0
}
# Now you can use template_data with the generate-pdf-from-template endpoint
```
## Error Responses
### 401 Unauthorized
```json theme={null}
{
"message": "Unauthorized"
}
```
**Occurs when:** The API key is missing or invalid.
### 404 Not Found
```json theme={null}
{
"message": "Couldnt find this template."
}
```
**Occurs when:** The requested template doesn't exist or isn't fully created yet.
### 500 Internal Server Error
```json theme={null}
{
"message": "Error message describing what went wrong"
}
```
**Occurs when:** Variable extraction fails or an internal server error prevents the operation from completing.
The template must be fully created and available before you can fetch its
variables. If you try to fetch variables for a template that's still being
created (status `ONGOING`), you'll receive a `404 Not Found` error.
# List Templates
Source: https://docs.pdfnoodle.com/api-reference/templates/list-templates
GET /v1/integration/templates
Retrieves a list of all templates available for the authenticated company.
GET [https://api.pdfnoodle.com/v1/integration/templates](https://api.pdfnoodle.com/v1/integration/templates)
## Request
```bash cURL theme={null}
curl --location 'https://api.pdfnoodle.com/v1/integration/templates' \
--header 'Authorization: Bearer pdfnoodle_api_123456789'
```
```javascript JavaScript theme={null}
const response = await fetch(
"https://api.pdfnoodle.com/v1/integration/templates",
{
method: "GET",
headers: {
Authorization: "Bearer pdfnoodle_api_123456789",
},
}
);
const result = await response.json();
```
```python Python theme={null}
import requests
response = requests.get(
'https://api.pdfnoodle.com/v1/integration/templates',
headers={
'Authorization': 'Bearer pdfnoodle_api_123456789'
}
)
result = response.json()
```
## Response
```json Success (200 OK) theme={null}
{
"templates": [
{
"id": "invoice-template-001",
"displayName": "Standard Invoice"
},
{
"id": "receipt-template-002",
"displayName": "Receipt Template"
}
]
}
```
```json Empty Result (200 OK) theme={null}
{
"templates": []
}
```
This endpoint responds with `200 OK` and returns a list of all templates available for your company. Templates are returned sorted alphabetically by `displayName` in ascending order.
## Response Fields
List of template objects. Returns an empty array `[]` if no templates exist
for the company.
Template identifier (this is the template `name` field, used for fetching
templates and as the `templateId` parameter in other endpoints)
Human-readable template name
## Usage
This endpoint is useful for:
* Displaying available templates in a UI
* Building template selection interfaces
* Discovering which templates are available before fetching details
* Getting template IDs to use with other endpoints
The `id` field in the response corresponds to the template's `name` field in
the database. Use this value as the `templateId` or `name` parameter when
fetching a specific template or its variables.
## Usage Example
Here's a complete example of listing templates and then fetching details for a specific template:
```javascript theme={null}
// List all templates
const listResponse = await fetch(
"https://api.pdfnoodle.com/v1/integration/templates",
{
headers: { Authorization: "Bearer pdfnoodle_api_123456789" },
}
);
const { templates } = await listResponse.json();
console.log("Available templates:", templates);
// Get the first template's ID
if (templates.length > 0) {
const templateId = templates[0].id;
console.log("First template ID:", templateId);
// Fetch template details
const templateResponse = await fetch(
`https://api.pdfnoodle.com/v1/integration/templates/${templateId}`,
{
headers: { Authorization: "Bearer pdfnoodle_api_123456789" },
}
);
const templateData = await templateResponse.json();
console.log("Template details:", templateData);
}
```
```python theme={null}
import requests
# List all templates
response = requests.get(
'https://api.pdfnoodle.com/v1/integration/templates',
headers={'Authorization': 'Bearer pdfnoodle_api_123456789'}
)
result = response.json()
templates = result['templates']
print('Available templates:', templates)
# Get the first template's ID
if templates:
template_id = templates[0]['id']
print('First template ID:', template_id)
# Fetch template details
template_response = requests.get(
f'https://api.pdfnoodle.com/v1/integration/templates/{template_id}',
headers={'Authorization': 'Bearer pdfnoodle_api_123456789'}
)
template_data = template_response.json()
print('Template details:', template_data)
```
## Error Responses
### 401 Unauthorized
```json theme={null}
{
"message": "Unauthorized"
}
```
**Occurs when:** The API key is missing or invalid.
### 500 Internal Server Error
```json theme={null}
{
"message": "Couldnt get templates"
}
```
**Occurs when:** An internal server error prevents the operation from completing.
# Compress PDF
Source: https://docs.pdfnoodle.com/api-reference/tools/compress-pdf
POST /v1/tools/compress-pdf
Compress a PDF file to reduce its size. Choose between three compression levels depending on your quality requirements.
This tool requires a publicly accessible PDF URL. If your file is stored locally or in memory, you can upload it to our temporary bucket first using the [Get Signed Upload URL](/api-reference/tools/get-signed-upload-url) endpoint.
POST [https://api.pdfnoodle.com/v1/tools/compress-pdf](https://api.pdfnoodle.com/v1/tools/compress-pdf)
## Request
```bash cURL theme={null}
curl --location 'https://api.pdfnoodle.com/v1/tools/compress-pdf' \
--header 'Authorization: Bearer pdfnoodle_api_123456789' \
--header 'Content-Type: application/json' \
--data '{
"url": "https://example.com/large-document.pdf",
"compressLevel": "medium",
"finalFilename": "compressed-document.pdf",
"expiration": 3600
}'
```
```javascript JavaScript theme={null}
const response = await fetch(
"https://api.pdfnoodle.com/v1/tools/compress-pdf",
{
method: "POST",
headers: {
Authorization: "Bearer pdfnoodle_api_123456789",
"Content-Type": "application/json",
},
body: JSON.stringify({
url: "https://example.com/large-document.pdf",
compressLevel: "medium",
finalFilename: "compressed-document.pdf",
expiration: 3600,
}),
}
);
const result = await response.json();
```
```python Python theme={null}
import requests
response = requests.post(
'https://api.pdfnoodle.com/v1/tools/compress-pdf',
headers={
'Authorization': 'Bearer pdfnoodle_api_123456789',
'Content-Type': 'application/json'
},
json={
'url': 'https://example.com/large-document.pdf',
'compressLevel': 'medium',
'finalFilename': 'compressed-document.pdf',
'expiration': 3600
}
)
result = response.json()
```
## Response
```json Success (200 OK) theme={null}
{
"status": "SUCCESS",
"url": "https://s3.amazonaws.com/...",
"fileName": "compressed-document.pdf",
"urlValidUntil": "2025-01-01T02:00:00.000Z",
"metadata": {
"originalSize": "2.5 MB",
"compressedSize": "1.2 MB",
"reduction": "52%"
}
}
```
```json Validation Error (400 Bad Request) theme={null}
{
"message": "compressLevel is Invalid enum value. Expected 'low' | 'medium' | 'high', received 'ultra'"
}
```
The response includes a `metadata` object with compression details so you can see exactly how much the file size was reduced.
The compressed file is stored persistently and can be re-downloaded at any time from the [dashboard logs](https://app.pdfnoodle.com/logs).
## Compression Levels
| Level | Quality | Best for |
| -------- | ---------------------------------- | ----------------------------------- |
| `low` | Highest quality, least compression | Print-ready documents |
| `medium` | Balanced quality and size | General use, email attachments |
| `high` | Most compression, lower quality | Web viewing, maximum size reduction |
The `medium` compression level is the default and works well for most use cases. Use `low` when you need to preserve maximum quality (e.g., print materials) and `high` when file size is the priority.
## Operation Tracking
Each compress operation creates a record in your dashboard logs, allowing you to:
* View all past compress operations with their status and metadata
* Re-download compressed files at any time via the logs table
* Track usage across your team
**Business and Scale plans**: Compress operations are unlimited and do not count toward your PDF generation quota.
**Starter plans**: Compress operations count toward your total volume quota.
## Async Mode
By default, the request waits for the compression to complete before returning a response. If you set `async: true`, the endpoint returns immediately with a `requestId` and `statusUrl` that you can use to poll for the result.
```json Async Response (200 OK) theme={null}
{
"requestId": "pdfnoodle_request_123456789",
"statusUrl": "https://api.pdfnoodle.com/v1/tools/status/pdfnoodle_request_123456789",
"message": "The tool is being executed asynchronously. Check the status using the status URL."
}
```
Use the [Get Tool Status](/api-reference/tools/get-tool-status) endpoint to check the result.
### Request Timeout (>30 seconds)
Even without `async: true`, if the operation takes more than 30 seconds it will automatically be processed in the background.
If the compression takes longer than 30 seconds, you'll receive a `202 Accepted` response with a `requestId` and `statusUrl` to poll for the result:
```json Timeout (202 Accepted) theme={null}
{
"requestId": "pdfnoodle_request_123456789",
"statusUrl": "https://api.pdfnoodle.com/v1/tools/status/pdfnoodle_request_123456789",
"message": "Couldn't complete the operation within 30 seconds, it is being processed asynchronously. Check the status using the status URL."
}
```
## Parameters
A valid, publicly accessible URL pointing to the PDF file you want to
compress.
The compression intensity. Must be one of: `"low"`, `"medium"`, or `"high"`.
Default: `"medium"`.
The desired filename for the compressed PDF. Must end with `.pdf`. If not
provided, the original filename from the URL will be used, or a random name
will be generated.
Number of seconds that the generated signed URL will take to expire. Must be
between 60 (1 minute) and 604800 (7 days). Default: 3600 (1 hour).
If `true`, the request returns immediately with a `requestId` and `statusUrl`
instead of waiting for the operation to complete. You can then poll the
[Get Tool Status](/api-reference/tools/get-tool-status) endpoint to check
when the result is ready.
# Convert Markdown to PDF
Source: https://docs.pdfnoodle.com/api-reference/tools/convert-markdown-to-pdf
POST /v1/tools/convert-markdown-to-pdf
Convert Markdown text into a beautifully styled PDF document. Supports full Markdown syntax including headings, lists, tables, code blocks with syntax highlighting, and more.
POST [https://api.pdfnoodle.com/v1/tools/convert-markdown-to-pdf](https://api.pdfnoodle.com/v1/tools/convert-markdown-to-pdf)
## Request
````bash cURL theme={null}
curl --location 'https://api.pdfnoodle.com/v1/tools/convert-markdown-to-pdf' \
--header 'Authorization: Bearer pdfnoodle_api_123456789' \
--header 'Content-Type: application/json' \
--data '{
"markdown": "# My Report\n\nThis is a **bold** statement.\n\n## Section 1\n\n- Item 1\n- Item 2\n\n```javascript\nconsole.log(\"Hello world\");\n```",
"pdfOptions": {
"format": "A4",
"margin": {
"top": "20mm",
"right": "15mm",
"bottom": "20mm",
"left": "15mm"
}
},
"finalFilename": "my-report.pdf",
"expiration": 3600
}'
````
```javascript JavaScript theme={null}
const response = await fetch(
"https://api.pdfnoodle.com/v1/tools/convert-markdown-to-pdf",
{
method: "POST",
headers: {
Authorization: "Bearer pdfnoodle_api_123456789",
"Content-Type": "application/json",
},
body: JSON.stringify({
markdown:
"# My Report\n\nThis is a **bold** statement.\n\n## Section 1\n\n- Item 1\n- Item 2",
pdfOptions: {
format: "A4",
margin: {
top: "20mm",
right: "15mm",
bottom: "20mm",
left: "15mm",
},
},
finalFilename: "my-report.pdf",
expiration: 3600,
}),
}
);
const result = await response.json();
```
```python Python theme={null}
import requests
response = requests.post(
'https://api.pdfnoodle.com/v1/tools/convert-markdown-to-pdf',
headers={
'Authorization': 'Bearer pdfnoodle_api_123456789',
'Content-Type': 'application/json'
},
json={
'markdown': '# My Report\n\nThis is a **bold** statement.\n\n## Section 1\n\n- Item 1\n- Item 2',
'pdfOptions': {
'format': 'A4',
'margin': {
'top': '20mm',
'right': '15mm',
'bottom': '20mm',
'left': '15mm'
}
},
'finalFilename': 'my-report.pdf',
'expiration': 3600
}
)
result = response.json()
```
## Response
```json Success (200 OK) theme={null}
{
"status": "SUCCESS",
"url": "https://s3.amazonaws.com/...",
"fileName": "my-report.pdf",
"urlValidUntil": "2025-01-01T02:00:00.000Z"
}
```
```json Validation Error (400 Bad Request) theme={null}
{
"message": "markdown is markdown content cannot be empty"
}
```
The response contains a `url` pointing to the generated PDF file. While the URL in the response expires after the time specified in `expiration` (default: 1 hour), the file itself is stored persistently and can be downloaded at any time from the [dashboard logs](https://app.pdfnoodle.com/logs).
## Supported Markdown Features
The converter supports the full Markdown specification including:
* **Headings** (H1 through H6)
* **Text formatting** (bold, italic, strikethrough)
* **Lists** (ordered and unordered, nested)
* **Tables**
* **Code blocks** with syntax highlighting (powered by Prism.js)
* **Inline code**
* **Links and images**
* **Blockquotes**
* **Horizontal rules**
Code blocks include automatic syntax highlighting for all major programming languages. Just specify the language after the opening triple backticks (e.g., ` ```javascript `).
## Operation Tracking
Each markdown conversion creates a record in your dashboard logs, allowing you to:
* View all past conversions with their status and metadata
* Re-download generated PDFs at any time via the logs table
* Track usage across your team
**Business and Scale plans**: Markdown conversion operations are unlimited and do not count toward your PDF generation quota.
**Starter plans**: Markdown conversion operations count toward your total volume quota.
## Async Mode
By default, the request waits for the conversion to complete before returning a response. If you set `async: true`, the endpoint returns immediately with a `requestId` and `statusUrl` that you can use to poll for the result.
```json Async Response (200 OK) theme={null}
{
"requestId": "pdfnoodle_request_123456789",
"statusUrl": "https://api.pdfnoodle.com/v1/tools/status/pdfnoodle_request_123456789",
"message": "The tool is being executed asynchronously. Check the status using the status URL."
}
```
Use the [Get Tool Status](/api-reference/tools/get-tool-status) endpoint to check the result.
### Request Timeout (>30 seconds)
Even without `async: true`, if the operation takes more than 30 seconds it will automatically be processed in the background.
If the conversion takes longer than 30 seconds, you'll receive a `202 Accepted` response with a `requestId` and `statusUrl` to poll for the result:
```json Timeout (202 Accepted) theme={null}
{
"requestId": "pdfnoodle_request_123456789",
"statusUrl": "https://api.pdfnoodle.com/v1/tools/status/pdfnoodle_request_123456789",
"message": "Couldn't complete the operation within 30 seconds, it is being processed asynchronously. Check the status using the status URL."
}
```
## Custom CSS
You can inject custom CSS to override the default styling:
```json theme={null}
{
"markdown": "# Custom Styled Report",
"customCss": "body { font-family: 'Georgia', serif; } h1 { color: #2563eb; border-bottom: 2px solid #2563eb; }"
}
```
## Parameters
The Markdown content to convert to PDF. Cannot be empty.
Custom CSS to apply to the rendered PDF. This will be injected alongside the
default styles, allowing you to override any default styling.
Configuration options for the PDF output. See fields below.
Paper format. One of: `Letter`, `Legal`, `Tabloid`, `Ledger`, `A0`, `A1`,
`A2`, `A3`, `A4`, `A5`, `A6`.
Set to `true` to use landscape orientation.
Scale of the webpage rendering. Must be between `0.1` and `2`.
Set to `true` to print background graphics and colors.
Paper ranges to print, e.g., `"1-5"` or `"1,3,5-7"`. An empty string means
all pages are printed.
Page margins. Each value accepts a number (in pixels) or a string with units
(`px`, `in`, `cm`, `mm`).
Top margin. Default: `0`
Right margin. Default: `0`
Bottom margin. Default: `0`
Left margin. Default: `0`
The desired filename for the generated PDF. Must end with `.pdf`. If not
provided, a random filename will be generated.
Number of seconds that the generated signed URL will take to expire. Must be
between 60 (1 minute) and 604800 (7 days). Default: 3600 (1 hour).
If `true`, the request returns immediately with a `requestId` and `statusUrl`
instead of waiting for the operation to complete. You can then poll the
[Get Tool Status](/api-reference/tools/get-tool-status) endpoint to check
when the result is ready.
# Get Signed Upload URL
Source: https://docs.pdfnoodle.com/api-reference/tools/get-signed-upload-url
GET /v1/tools/get-signed-upload-url
Generate a pair of presigned URLs: one for uploading a PDF file to temporary storage and one for downloading it.
GET [https://api.pdfnoodle.com/v1/tools/get-signed-upload-url](https://api.pdfnoodle.com/v1/tools/get-signed-upload-url)
## Request
```bash cURL theme={null}
curl --location 'https://api.pdfnoodle.com/v1/tools/get-signed-upload-url?fileName=my-document.pdf' \
--header 'Authorization: Bearer pdfnoodle_api_123456789'
```
```javascript JavaScript theme={null}
const response = await fetch(
"https://api.pdfnoodle.com/v1/tools/get-signed-upload-url?fileName=my-document.pdf",
{
method: "GET",
headers: {
Authorization: "Bearer pdfnoodle_api_123456789",
},
}
);
const result = await response.json();
```
```python Python theme={null}
import requests
response = requests.get(
'https://api.pdfnoodle.com/v1/tools/get-signed-upload-url',
headers={
'Authorization': 'Bearer pdfnoodle_api_123456789'
},
params={
'fileName': 'my-document.pdf'
}
)
result = response.json()
```
## Response
```json Success (200 OK) theme={null}
{
"presignedUploadUrl": "https://s3.amazonaws.com/...",
"presignedGetUrl": "https://s3.amazonaws.com/...",
"status": "SUCCESS",
"fileName": "my-document.pdf",
"uploadUrlValidUntil": "2025-01-01T01:30:00.000Z",
"getUrlValidUntil": "2025-01-01T02:00:00.000Z"
}
```
```json Error (400 Bad Request) theme={null}
{
"message": "Couldnt identify company"
}
```
The `presignedUploadUrl` is a temporary URL you can use to upload your PDF file via a `PUT` request. The `presignedGetUrl` is a temporary URL you can use to download the uploaded file.
* **Upload URL** expires in **30 minutes**
* **Download URL** expires in **60 minutes**
If no `fileName` is provided, a random filename will be generated automatically. Filenames are sanitized to only allow alphanumeric characters, dots, hyphens, and underscores.
### Uploading a file using the presigned URL
Once you have the `presignedUploadUrl`, you can upload your PDF file using a `PUT` request:
```bash cURL theme={null}
curl --request PUT \
--url 'YOUR_PRESIGNED_UPLOAD_URL' \
--header 'Content-Type: application/pdf' \
--data-binary '@/path/to/your/file.pdf'
```
```javascript JavaScript theme={null}
const fileBuffer = fs.readFileSync("/path/to/your/file.pdf");
await fetch("YOUR_PRESIGNED_UPLOAD_URL", {
method: "PUT",
headers: {
"Content-Type": "application/pdf",
},
body: fileBuffer,
});
```
```python Python theme={null}
with open('/path/to/your/file.pdf', 'rb') as f:
requests.put(
'YOUR_PRESIGNED_UPLOAD_URL',
headers={'Content-Type': 'application/pdf'},
data=f.read()
)
```
### Accessing the uploaded file
After uploading, use the `presignedGetUrl` from the original response to download or access the file:
```bash cURL theme={null}
curl --location 'YOUR_PRESIGNED_GET_URL' --output downloaded-file.pdf
```
```javascript JavaScript theme={null}
const response = await fetch("YOUR_PRESIGNED_GET_URL");
const blob = await response.blob();
// Save to file (Node.js)
const buffer = Buffer.from(await blob.arrayBuffer());
fs.writeFileSync("downloaded-file.pdf", buffer);
```
```python Python theme={null}
response = requests.get('YOUR_PRESIGNED_GET_URL')
with open('downloaded-file.pdf', 'wb') as f:
f.write(response.content)
```
The `presignedGetUrl` is also the URL you should pass as the `url` parameter to other PDF Tools endpoints (such as [Merge PDFs](/api-reference/tools/merge-pdfs), [Split PDF](/api-reference/tools/split-pdf), [Compress PDF](/api-reference/tools/compress-pdf), or [Update PDF Metadata](/api-reference/tools/update-pdf-metadata)) when you want to process the uploaded file.
## Parameters
The desired filename for the uploaded PDF. If not provided, a random name will
be generated. Special characters will be replaced with underscores.
# Get Tool Status
Source: https://docs.pdfnoodle.com/api-reference/tools/get-tool-status
GET /v1/tools/status/:requestId
Retrieve the status of a tool operation using the requestId returned when using async mode or when a synchronous request times out.
Use this endpoint to check the status of a tool operation. This is particularly useful when:
You've set `async: true` and want to poll for the result
A synchronous request timed out (>30 seconds) and was automatically processed in the background
GET [https://api.pdfnoodle.com/v1/tools/status/:requestId](https://api.pdfnoodle.com/v1/tools/status/:requestId)
## Request
```bash cURL theme={null}
curl --location 'https://api.pdfnoodle.com/v1/tools/status/pdfnoodle_request_123456789' \
--header 'Authorization: Bearer pdfnoodle_api_123456789'
```
```javascript JavaScript theme={null}
const response = await fetch(
"https://api.pdfnoodle.com/v1/tools/status/pdfnoodle_request_123456789",
{
method: "GET",
headers: {
Authorization: "Bearer pdfnoodle_api_123456789",
},
}
);
const result = await response.json();
```
```python Python theme={null}
import requests
response = requests.get(
'https://api.pdfnoodle.com/v1/tools/status/pdfnoodle_request_123456789',
headers={
'Authorization': 'Bearer pdfnoodle_api_123456789'
}
)
result = response.json()
```
## Response
The endpoint responds with `200 OK` and returns the current status of the tool operation:
```json ONGOING theme={null}
{
"requestId": "pdfnoodle_request_123456789",
"status": "ONGOING",
"result": null,
"metadata": {}
}
```
```json SUCCESS theme={null}
{
"requestId": "pdfnoodle_request_123456789",
"status": "SUCCESS",
"result": {
"url": "https://s3.amazonaws.com/...",
"fileName": "merged-report.pdf",
"urlValidUntil": "2025-01-01T02:00:00.000Z"
},
"metadata": {
"toolName": "mergePdfs"
}
}
```
```json FAILED theme={null}
{
"requestId": "pdfnoodle_request_123456789",
"status": "FAILED",
"result": null,
"metadata": {
"errorMessage": "Error while merging PDFs",
"toolName": "mergePdfs"
}
}
```
```json Not Found (404) theme={null}
{
"message": "Couldn't find this request in queue"
}
```
The `result` object contains the same response body you would receive from a synchronous tool request. For example, a merge operation returns `url`, `fileName`, and `urlValidUntil`, while a split operation returns `urls`, `totalParts`, etc.
## Parameters
The requestId returned from an async tool request or from a synchronous
request that timed out.
## Response Fields
The unique identifier for this tool operation request.
The current status of the tool operation. Possible values:
* `ONGOING` - The operation is still being processed
* `SUCCESS` - The operation completed successfully
* `FAILED` - The operation failed
The result of the tool operation. This will be `null` when `status` is
`ONGOING` or `FAILED`. When `SUCCESS`, it contains the same fields as the
synchronous response for the specific tool used.
Additional metadata about the operation. Contains:
* **toolName** - The name of the tool that was executed (present on `SUCCESS` and `FAILED`)
* **errorMessage** - Error description (only present when `status` is `FAILED`)
If the `status` is `FAILED`, check the `metadata.errorMessage` for details
on what went wrong. Contact support if the issue persists.
## Usage Example
Here's a complete example of using async mode with polling:
```javascript theme={null}
// Step 1: Make an async tool request
const response = await fetch("https://api.pdfnoodle.com/v1/tools/merge-pdfs", {
method: "POST",
headers: {
Authorization: "Bearer pdfnoodle_api_123456789",
"Content-Type": "application/json",
},
body: JSON.stringify({
urls: [
"https://example.com/document-1.pdf",
"https://example.com/document-2.pdf",
],
async: true,
}),
});
const { requestId, statusUrl } = await response.json();
// Step 2: Poll for the result
async function pollForResult(requestId) {
const maxAttempts = 60;
let attempts = 0;
while (attempts < maxAttempts) {
const statusResponse = await fetch(
`https://api.pdfnoodle.com/v1/tools/status/${requestId}`,
{
method: "GET",
headers: {
Authorization: "Bearer pdfnoodle_api_123456789",
},
}
);
const result = await statusResponse.json();
if (result.status === "SUCCESS") {
return result.result;
}
if (result.status === "FAILED") {
throw new Error(result.metadata.errorMessage);
}
await new Promise((resolve) => setTimeout(resolve, 5000));
attempts++;
}
throw new Error("Operation timed out");
}
const result = await pollForResult(requestId);
console.log("Merged PDF URL:", result.url);
```
```python theme={null}
import requests
import time
# Step 1: Make an async tool request
response = requests.post(
'https://api.pdfnoodle.com/v1/tools/merge-pdfs',
headers={
'Authorization': 'Bearer pdfnoodle_api_123456789',
'Content-Type': 'application/json'
},
json={
'urls': [
'https://example.com/document-1.pdf',
'https://example.com/document-2.pdf'
],
'async': True
}
)
data = response.json()
request_id = data['requestId']
# Step 2: Poll for the result
def poll_for_result(request_id):
max_attempts = 60
attempts = 0
while attempts < max_attempts:
status_response = requests.get(
f'https://api.pdfnoodle.com/v1/tools/status/{request_id}',
headers={
'Authorization': 'Bearer pdfnoodle_api_123456789'
}
)
result = status_response.json()
if result['status'] == 'SUCCESS':
return result['result']
if result['status'] == 'FAILED':
raise Exception(result['metadata']['errorMessage'])
time.sleep(5)
attempts += 1
raise Exception('Operation timed out')
result = poll_for_result(request_id)
print('Merged PDF URL:', result['url'])
```
# Merge PDFs
Source: https://docs.pdfnoodle.com/api-reference/tools/merge-pdfs
POST /v1/tools/merge-pdfs
Combine two or more PDF files into a single document. The PDFs are merged in the order they are provided.
This tool requires publicly accessible PDF URLs. If your files are stored locally or in memory, you can upload them to our temporary bucket first using the [Get Signed Upload URL](/api-reference/tools/get-signed-upload-url) endpoint.
POST [https://api.pdfnoodle.com/v1/tools/merge-pdfs](https://api.pdfnoodle.com/v1/tools/merge-pdfs)
## Request
```bash cURL theme={null}
curl --location 'https://api.pdfnoodle.com/v1/tools/merge-pdfs' \
--header 'Authorization: Bearer pdfnoodle_api_123456789' \
--header 'Content-Type: application/json' \
--data '{
"urls": [
"https://example.com/document-1.pdf",
"https://example.com/document-2.pdf",
"https://example.com/document-3.pdf"
],
"finalFilename": "merged-report.pdf",
"expiration": 3600
}'
```
```javascript JavaScript theme={null}
const response = await fetch("https://api.pdfnoodle.com/v1/tools/merge-pdfs", {
method: "POST",
headers: {
Authorization: "Bearer pdfnoodle_api_123456789",
"Content-Type": "application/json",
},
body: JSON.stringify({
urls: [
"https://example.com/document-1.pdf",
"https://example.com/document-2.pdf",
"https://example.com/document-3.pdf",
],
finalFilename: "merged-report.pdf",
expiration: 3600,
}),
});
const result = await response.json();
```
```python Python theme={null}
import requests
response = requests.post(
'https://api.pdfnoodle.com/v1/tools/merge-pdfs',
headers={
'Authorization': 'Bearer pdfnoodle_api_123456789',
'Content-Type': 'application/json'
},
json={
'urls': [
'https://example.com/document-1.pdf',
'https://example.com/document-2.pdf',
'https://example.com/document-3.pdf'
],
'finalFilename': 'merged-report.pdf',
'expiration': 3600
}
)
result = response.json()
```
## Response
```json Success (200 OK) theme={null}
{
"status": "SUCCESS",
"url": "https://s3.amazonaws.com/...",
"fileName": "merged-report.pdf",
"urlValidUntil": "2025-01-01T02:00:00.000Z"
}
```
```json Validation Error (400 Bad Request) theme={null}
{
"message": "urls is at least 2 PDF URLs are required"
}
```
The response contains a `url` pointing to the merged PDF file. While the URL in the response expires after the time specified in `expiration` (default: 1 hour), the file itself is stored persistently and can be downloaded at any time from the [dashboard logs](https://app.pdfnoodle.com/logs).
PDFs are merged in the exact order they appear in the `urls` array. Make sure to order them correctly.
## Operation Tracking
Each merge operation creates a record in your dashboard logs, allowing you to:
* View all past merge operations with their status and metadata
* Re-download merged files at any time via the logs table
* Track usage across your team
**Business and Scale plans**: Merge operations are unlimited and do not count toward your PDF generation quota.
**Starter plans**: Merge operations count toward your total volume quota.
## Async Mode
By default, the request waits for the merge to complete before returning a response. If you set `async: true`, the endpoint returns immediately with a `requestId` and `statusUrl` that you can use to poll for the result.
```json Async Response (200 OK) theme={null}
{
"requestId": "pdfnoodle_request_123456789",
"statusUrl": "https://api.pdfnoodle.com/v1/tools/status/pdfnoodle_request_123456789",
"message": "The tool is being executed asynchronously. Check the status using the status URL."
}
```
Use the [Get Tool Status](/api-reference/tools/get-tool-status) endpoint to check the result.
### Request Timeout (>30 seconds)
Even without `async: true`, if the operation takes more than 30 seconds it will automatically be processed in the background.
If the merge takes longer than 30 seconds, you'll receive a `202 Accepted` response with a `requestId` and `statusUrl` to poll for the result:
```json Timeout (202 Accepted) theme={null}
{
"requestId": "pdfnoodle_request_123456789",
"statusUrl": "https://api.pdfnoodle.com/v1/tools/status/pdfnoodle_request_123456789",
"message": "Couldn't complete the operation within 30 seconds, it is being processed asynchronously. Check the status using the status URL."
}
```
## Parameters
An array of URLs pointing to the PDF files you want to merge. A minimum of 2
URLs is required. Each URL must be a valid, publicly accessible URL.
The desired filename for the merged PDF. Must end with `.pdf`. If not
provided, a random filename will be generated.
Number of seconds that the generated signed URL will take to expire. Must be
between 60 (1 minute) and 604800 (7 days). Default: 3600 (1 hour).
If `true`, the request returns immediately with a `requestId` and `statusUrl`
instead of waiting for the operation to complete. You can then poll the
[Get Tool Status](/api-reference/tools/get-tool-status) endpoint to check
when the result is ready.
# Split PDF
Source: https://docs.pdfnoodle.com/api-reference/tools/split-pdf
POST /v1/tools/split-pdf
Split a single PDF file into multiple smaller PDFs. You can split by specific page ranges or at regular intervals.
This tool requires a publicly accessible PDF URL. If your file is stored locally or in memory, you can upload it to our temporary bucket first using the [Get Signed Upload URL](/api-reference/tools/get-signed-upload-url) endpoint.
POST [https://api.pdfnoodle.com/v1/tools/split-pdf](https://api.pdfnoodle.com/v1/tools/split-pdf)
## Request
```bash cURL theme={null}
# Split by intervals (every 2 pages)
curl --location 'https://api.pdfnoodle.com/v1/tools/split-pdf' \
--header 'Authorization: Bearer pdfnoodle_api_123456789' \
--header 'Content-Type: application/json' \
--data '{
"url": "https://example.com/large-document.pdf",
"splitMode": "intervals",
"interval": 2,
"finalFilename": "chapter.pdf",
"expiration": 3600
}'
```
```javascript JavaScript theme={null}
const response = await fetch("https://api.pdfnoodle.com/v1/tools/split-pdf", {
method: "POST",
headers: {
Authorization: "Bearer pdfnoodle_api_123456789",
"Content-Type": "application/json",
},
body: JSON.stringify({
url: "https://example.com/large-document.pdf",
splitMode: "intervals",
interval: 2,
finalFilename: "chapter.pdf",
expiration: 3600,
}),
});
const result = await response.json();
```
```python Python theme={null}
import requests
response = requests.post(
'https://api.pdfnoodle.com/v1/tools/split-pdf',
headers={
'Authorization': 'Bearer pdfnoodle_api_123456789',
'Content-Type': 'application/json'
},
json={
'url': 'https://example.com/large-document.pdf',
'splitMode': 'intervals',
'interval': 2,
'finalFilename': 'chapter.pdf',
'expiration': 3600
}
)
result = response.json()
```
## Response
```json Success (200 OK) theme={null}
{
"status": "SUCCESS",
"totalParts": 3,
"fileName": "chapter.pdf",
"urlValidUntil": "2025-01-01T02:00:00.000Z",
"urls": [
"https://s3.amazonaws.com/.../chapter_1.pdf",
"https://s3.amazonaws.com/.../chapter_2.pdf",
"https://s3.amazonaws.com/.../chapter_3.pdf"
],
"zipUrl": "https://s3.amazonaws.com/.../chapter.zip"
}
```
```json Validation Error (400 Bad Request) theme={null}
{
"message": "ranges is ranges is required when splitMode is \"ranges\""
}
```
The response contains an array of `urls`, each pointing to a split part of the original PDF. Parts are named using the pattern `{filename}_{partNumber}.pdf` (e.g., `chapter_1.pdf`, `chapter_2.pdf`).
A `zipUrl` is also included, which points to a `.zip` archive containing all split parts for convenient bulk download.
All files are stored persistently and can be re-downloaded at any time from the [dashboard logs](https://app.pdfnoodle.com/logs).
## Split Modes
There are two ways to split a PDF:
### Intervals Mode (default)
Splits the PDF every N pages. For example, a 10-page PDF with `interval: 3` will produce 4 parts: pages 1-3, 4-6, 7-9, and 10.
```json theme={null}
{
"url": "https://example.com/document.pdf",
"splitMode": "intervals",
"interval": 3
}
```
### Ranges Mode
Splits the PDF by specific page ranges. Use comma-separated ranges like `1-3,5,7-10`.
```json theme={null}
{
"url": "https://example.com/document.pdf",
"splitMode": "ranges",
"ranges": "1-3,5,7-10"
}
```
When using `splitMode: "ranges"`, the `ranges` parameter is required. When using `splitMode: "intervals"`, the `interval` parameter is required.
## Operation Tracking
Each split operation creates a record in your dashboard logs, allowing you to:
* View all past split operations with their status and metadata
* Re-download split files at any time via the logs table
* Track usage across your team
**Business and Scale plans**: Split operations are unlimited and do not count toward your PDF generation quota.
**Starter plans**: Split operations count toward your total volume quota.
## Async Mode
By default, the request waits for the split to complete before returning a response. If you set `async: true`, the endpoint returns immediately with a `requestId` and `statusUrl` that you can use to poll for the result.
```json Async Response (200 OK) theme={null}
{
"requestId": "pdfnoodle_request_123456789",
"statusUrl": "https://api.pdfnoodle.com/v1/tools/status/pdfnoodle_request_123456789",
"message": "The tool is being executed asynchronously. Check the status using the status URL."
}
```
Use the [Get Tool Status](/api-reference/tools/get-tool-status) endpoint to check the result.
### Request Timeout (>30 seconds)
Even without `async: true`, if the operation takes more than 30 seconds it will automatically be processed in the background.
If the split takes longer than 30 seconds, you'll receive a `202 Accepted` response with a `requestId` and `statusUrl` to poll for the result:
```json Timeout (202 Accepted) theme={null}
{
"requestId": "pdfnoodle_request_123456789",
"statusUrl": "https://api.pdfnoodle.com/v1/tools/status/pdfnoodle_request_123456789",
"message": "Couldn't complete the operation within 30 seconds, it is being processed asynchronously. Check the status using the status URL."
}
```
## Parameters
A valid, publicly accessible URL pointing to the PDF file you want to split.
The method to use for splitting. Must be either `"ranges"` or `"intervals"`.
Default: `"intervals"`.
Specific page ranges to extract (e.g., `"1-3,5,7-10"`). Required when
`splitMode` is `"ranges"`.
Split the PDF every N pages. Must be at least 1. Required when `splitMode` is
`"intervals"`. Default: `1`.
The base filename for the split parts. Must end with `.pdf`. Each part will be
named `{basename}_{partNumber}.pdf`. If not provided, the original filename
from the URL will be used, or a random name will be generated.
Number of seconds that the generated signed URLs will take to expire. Must be
between 60 (1 minute) and 604800 (7 days). Default: 3600 (1 hour).
If `true`, the request returns immediately with a `requestId` and `statusUrl`
instead of waiting for the operation to complete. You can then poll the
[Get Tool Status](/api-reference/tools/get-tool-status) endpoint to check
when the result is ready.
# Update PDF Metadata
Source: https://docs.pdfnoodle.com/api-reference/tools/update-pdf-metadata
POST /v1/tools/update-pdf-metadata
Update the embedded metadata of a PDF file, such as title, author, subject, keywords, and more. The original PDF content is preserved.
This tool requires a publicly accessible PDF URL. If your file is stored locally or in memory, you can upload it to our temporary bucket first using the [Get Signed Upload URL](/api-reference/tools/get-signed-upload-url) endpoint.
POST [https://api.pdfnoodle.com/v1/tools/update-pdf-metadata](https://api.pdfnoodle.com/v1/tools/update-pdf-metadata)
## Request
```bash cURL theme={null}
curl --location 'https://api.pdfnoodle.com/v1/tools/update-pdf-metadata' \
--header 'Authorization: Bearer pdfnoodle_api_123456789' \
--header 'Content-Type: application/json' \
--data '{
"url": "https://example.com/document.pdf",
"metadata": {
"title": "Annual Report 2025",
"author": "Acme Corp",
"subject": "Financial Summary",
"keywords": ["finance", "annual", "report"],
"creator": "Acme Document System"
},
"finalFilename": "annual-report-2025.pdf",
"expiration": 3600
}'
```
```javascript JavaScript theme={null}
const response = await fetch(
"https://api.pdfnoodle.com/v1/tools/update-pdf-metadata",
{
method: "POST",
headers: {
Authorization: "Bearer pdfnoodle_api_123456789",
"Content-Type": "application/json",
},
body: JSON.stringify({
url: "https://example.com/document.pdf",
metadata: {
title: "Annual Report 2025",
author: "Acme Corp",
subject: "Financial Summary",
keywords: ["finance", "annual", "report"],
creator: "Acme Document System",
},
finalFilename: "annual-report-2025.pdf",
expiration: 3600,
}),
}
);
const result = await response.json();
```
```python Python theme={null}
import requests
response = requests.post(
'https://api.pdfnoodle.com/v1/tools/update-pdf-metadata',
headers={
'Authorization': 'Bearer pdfnoodle_api_123456789',
'Content-Type': 'application/json'
},
json={
'url': 'https://example.com/document.pdf',
'metadata': {
'title': 'Annual Report 2025',
'author': 'Acme Corp',
'subject': 'Financial Summary',
'keywords': ['finance', 'annual', 'report'],
'creator': 'Acme Document System'
},
'finalFilename': 'annual-report-2025.pdf',
'expiration': 3600
}
)
result = response.json()
```
## Response
```json Success (200 OK) theme={null}
{
"status": "SUCCESS",
"url": "https://s3.amazonaws.com/...",
"fileName": "annual-report-2025.pdf",
"urlValidUntil": "2025-01-01T02:00:00.000Z"
}
```
```json Validation Error (400 Bad Request) theme={null}
{
"message": "url is must be a valid URL"
}
```
The response contains a `url` pointing to the updated PDF file. While the URL in the response expires after the time specified in `expiration` (default: 1 hour), the file itself is stored persistently and can be downloaded at any time from the [dashboard logs](https://app.pdfnoodle.com/logs). The original PDF content is unchanged - only the metadata fields you specified are updated.
On macOS, you can verify the updated metadata by right-clicking the downloaded PDF and selecting **Get Info** in Finder. On Windows, right-click the file and select **Properties > Details**.
## Operation Tracking
Each metadata update creates a record in your dashboard logs, allowing you to:
* View all past metadata operations with their status and metadata
* Re-download updated files at any time via the logs table
* Track usage across your team
**Business and Scale plans**: Metadata update operations are unlimited and do not count toward your PDF generation quota.
**Starter plans**: Metadata update operations count toward your total volume quota.
## Async Mode
By default, the request waits for the metadata update to complete before returning a response. If you set `async: true`, the endpoint returns immediately with a `requestId` and `statusUrl` that you can use to poll for the result.
```json Async Response (200 OK) theme={null}
{
"requestId": "pdfnoodle_request_123456789",
"statusUrl": "https://api.pdfnoodle.com/v1/tools/status/pdfnoodle_request_123456789",
"message": "The tool is being executed asynchronously. Check the status using the status URL."
}
```
Use the [Get Tool Status](/api-reference/tools/get-tool-status) endpoint to check the result.
### Request Timeout (>30 seconds)
Even without `async: true`, if the operation takes more than 30 seconds it will automatically be processed in the background.
If the operation takes longer than 30 seconds, you'll receive a `202 Accepted` response with a `requestId` and `statusUrl` to poll for the result:
```json Timeout (202 Accepted) theme={null}
{
"requestId": "pdfnoodle_request_123456789",
"statusUrl": "https://api.pdfnoodle.com/v1/tools/status/pdfnoodle_request_123456789",
"message": "Couldn't complete the operation within 30 seconds, it is being processed asynchronously. Check the status using the status URL."
}
```
## Parameters
A valid, publicly accessible URL pointing to the PDF file whose metadata you
want to update.
An object containing the metadata fields to update. At least one field should
be provided. See available fields below.
A human-readable title for the document.
The individual or organization that created the document.
A brief description of the document's topic.
A list of keywords describing the content.
The original software or system that created the content.
Copyright information for the document.
The creation date of the document (ISO date string).
The last modification date of the document (ISO date string).
The PDF specification version (e.g., `1.7`).
The software that produced the PDF.
Whether the PDF is marked (tagged) for accessibility.
Trapping status. Common values: `"Unknown"`, `"True"`, `"False"`.
The desired filename for the updated PDF. Must end with `.pdf`. If not
provided, the original filename from the URL will be used, or a random name
will be generated.
Number of seconds that the generated signed URL will take to expire. Must be
between 60 (1 minute) and 604800 (7 days). Default: 3600 (1 hour).
If `true`, the request returns immediately with a `requestId` and `statusUrl`
instead of waiting for the operation to complete. You can then poll the
[Get Tool Status](/api-reference/tools/get-tool-status) endpoint to check
when the result is ready.
# Bubble Integration
Source: https://docs.pdfnoodle.com/integrations/bubble/index
Generate PDFs in your Bubble app using pdf noodle
Integrate pdf noodle with [Bubble](https://bubble.io) to generate PDFs directly from your no-code application. Create invoices, reports, certificates, and more without writing code.
## Prerequisites
Before you begin, make sure you have:
* A pdf noodle account ([sign up here](https://app.pdfnoodle.com/auth/sign-up))
* Your API key from [API Settings](https://app.pdfnoodle.com/settings/api)
* At least one template created in pdf noodle
* A Bubble app
***
## Setting Up the API Connector
Bubble connects to pdf noodle using the **API Connector plugin**.
In your Bubble app, go to **Plugins** → **Add plugins** → Search for **API Connector** → **Install**
Open the API Connector plugin and click **Add another API**. Name it `pdfnoodle`.
Set up shared headers for all calls:
| Key | Value |
| --------------- | --------------------- |
| `Authorization` | `Bearer YOUR_API_KEY` |
| `Content-Type` | `application/json` |
Check **Private key in header** to keep your API key secure.
Click **Add another call** and configure:
| Setting | Value |
| ------------- | --------------------------------------- |
| **Name** | `generate_pdf` |
| **Method** | `POST` |
| **URL** | `https://api.pdfnoodle.com/v1/pdf/sync` |
| **Data type** | `JSON` |
| **Body type** | `JSON object` |
Add the request body:
```json theme={null}
{
"templateId": "",
"data": {
"customerName": "",
"invoiceNumber": "",
"total":
}
}
```
Mark each parameter (e.g., ``) as dynamic so you can pass values from your app.
Click **Initialize call** with test values to verify it works. You should see a response with the PDF URL.
***
## Finding Your Template ID
1. Go to [app.pdfnoodle.com](https://app.pdfnoodle.com)
2. Navigate to the **Templates** section
3. Copy the Template ID shown in the list next to your template
***
## Using in Workflows
Once the API is configured, use it in your Bubble workflows:
Create a workflow triggered by a button click or other event.
Add action: **Plugins** → **pdfnoodle - generate\_pdf**
Set the dynamic values:
| Parameter | Value |
| --------------- | ------------------------------------------ |
| `templateId` | Your template ID (static or from database) |
| `customerName` | `Current User's name` |
| `invoiceNumber` | `Current Order's id` |
| `total` | `Current Order's total` |
The result contains `signedUrl` with the PDF link. You can:
* Display it in a link element
* Open it in a new tab
* Save it to your database
***
## Example: Download Invoice Button
Create a button that generates and downloads an invoice:
**Workflow:**
1. **When** Button "Download Invoice" is clicked
2. **Action** pdfnoodle - generate\_pdf
* templateId: `your-invoice-template-id`
* customerName: `Current User's name`
* invoiceNumber: `Current Order's id`
* total: `Current Order's total`
3. **Action** Open external website
* URL: `Result of step 2's signedUrl`
***
## Displaying PDFs
You can display the generated PDF in your app:
**Using a Link:**
```
Link destination: Result of generate_pdf's signedUrl
```
**Using an iframe (for preview):**
Add an HTML element with:
```html theme={null}
```
***
## Error Handling
* Check your API key is correct
* Verify the template ID exists
* Ensure all required parameters are provided
* Check the Bubble debugger for error details
* Make sure you initialized the API call
* Check that the response is being captured correctly
* Verify your pdf noodle account has available credits
* Large PDFs take longer to generate
* Consider using the async endpoint for complex documents
* Show a loading indicator while generating
***
## API Endpoints
| Endpoint | Description |
| --------------------------- | ----------------------------------------- |
| `POST /v1/pdf/sync` | Generate PDF from template (synchronous) |
| `POST /v1/pdf/async` | Generate PDF from template (asynchronous) |
| `POST /v1/html-to-pdf/sync` | Convert HTML to PDF (synchronous) |
See the complete [API Reference](/api-reference) for all endpoints and options.
***
You can also generate PDFs using the API Connector plugin by calling the pdf noodle API directly. See the [API Reference](/api-reference) for all available endpoints and options.
## Resources
* [Bubble Integration Page](https://pdfnoodle.com/integrations/bubble) - pdf noodle + Bubble overview
* [Bubble Automation Guide](https://pdfnoodle.com/blog/how-to-automate-pdf-generation-with-bubble-and-pdforge) - Step-by-step tutorial
* [Bubble Manual](https://manual.bubble.io) - Official Bubble docs
* [API Connector Guide](https://manual.bubble.io/core-resources/bubble-made-plugins/api-connector) - Bubble API Connector docs
* [pdf noodle API Reference](/api-reference) - Full API documentation
# Make Integration
Source: https://docs.pdfnoodle.com/integrations/make/index
Automate PDF generation workflows by connecting Make with pdf noodle
Integrate pdf noodle with [Make](https://make.com) (formerly Integromat) to automate your PDF generation workflows. Connect to 1000+ apps and generate documents automatically.
## Prerequisites
Before you begin, make sure you have:
* A pdf noodle account ([sign up here](https://app.pdfnoodle.com/auth/sign-up))
* Your API key from [API Settings](https://app.pdfnoodle.com/settings/api)
* At least one template created in pdf noodle
* A Make account ([sign up here](https://make.com))
***
## Setting Up the Integration
Make connects to pdf noodle using the **HTTP module** to call the API directly.
In Make, click **Create a new scenario** and add your trigger (e.g., Google Sheets, Webhook, or any other app).
Click the **+** button and search for **HTTP**. Select **Make a request**.
Set up the HTTP module with these settings:
| Setting | Value |
| ---------------- | --------------------------------------- |
| **URL** | `https://api.pdfnoodle.com/v1/pdf/sync` |
| **Method** | `POST` |
| **Headers** | See below |
| **Body type** | `Raw` |
| **Content type** | `JSON (application/json)` |
**Headers:**
| Name | Value |
| --------------- | --------------------- |
| `Authorization` | `Bearer YOUR_API_KEY` |
| `Content-Type` | `application/json` |
Add the JSON body with your template ID and data:
```json theme={null}
{
"templateId": "your-template-id",
"data": {
"customerName": "{{1.customer_name}}",
"invoiceNumber": "{{1.invoice_id}}",
"items": {{1.items}},
"total": {{1.total}}
}
}
```
Use Make's variable picker to map fields from your trigger.
The response contains the PDF URL:
```json theme={null}
{
"signedUrl": "https://pdforge-production.s3.us-east-2.amazonaws.com/...",
"metadata": {
"executionTime": "1.805 seconds",
"fileSize": "4.066 kB"
}
}
```
Check **Parse response** to use the `signedUrl` in subsequent modules.
***
## Finding Your Template ID
1. Go to [app.pdfnoodle.com](https://app.pdfnoodle.com)
2. Navigate to the **Templates** section
3. Copy the Template ID shown in the list next to your template
***
## Example: Invoice on New Order
A typical scenario that generates an invoice when a new order is received:
1. **Trigger** - Shopify "Watch Orders" or Webhook
2. **HTTP Module** - Generate PDF with pdf noodle
3. **Email Module** - Send the PDF to the customer
***
## Using the PDF URL
After generating the PDF, you can use the URL to:
* **Send via Email** - Attach or link in Gmail, Outlook, etc.
* **Upload to Storage** - Save to Google Drive, Dropbox, S3
* **Update Records** - Add the link to Airtable, Notion, CRM
* **Send via Chat** - Share in Slack, Teams, Discord
***
## Error Handling
* Verify your API key is correct
* Ensure the Authorization header format is `Bearer YOUR_API_KEY`
* Check your JSON syntax is valid
* Ensure all required template variables are provided
* Verify the template ID exists
* Increase the HTTP module timeout in advanced settings
* Use the async endpoint for large PDFs: `https://api.pdfnoodle.com/v1/pdf/async`
***
## API Endpoints
| Endpoint | Description |
| --------------------------- | ----------------------------------------- |
| `POST /v1/pdf/sync` | Generate PDF from template (synchronous) |
| `POST /v1/pdf/async` | Generate PDF from template (asynchronous) |
| `POST /v1/html-to-pdf/sync` | Convert HTML to PDF (synchronous) |
See the complete [API Reference](/api-reference) for all endpoints and options.
***
You can also generate PDFs using any HTTP Request module by calling the pdf noodle API directly. See the [API Reference](/api-reference) for all available endpoints and options.
## Resources
* [Make Integration Page](https://pdfnoodle.com/integrations/make) - pdf noodle + Make overview
* [Make Automation Guide](https://pdfnoodle.com/blog/how-to-automate-pdf-generation-with-make-and-pdforge) - Step-by-step tutorial
* [Make Documentation](https://www.make.com/en/help) - Official Make docs
* [pdf noodle API Reference](/api-reference) - Full API documentation
# PDF Best Practices Skill
Source: https://docs.pdfnoodle.com/integrations/mcp/best-practices
Comprehensive guidelines for creating HTML that renders perfectly as PDF
When using the `html_to_pdf` tool, the quality of your output depends entirely on how well the HTML is structured for print. The **PDF Best Practices** skill provides AI assistants with comprehensive guidelines to create professional, well-formatted PDF documents.
```
HTML Content ──▶ CSS Styles ──▶ PDF Engine ──▶ Page Layout ──▶ Final PDF
(Puppeteer) & Margins
```
## Installing the Skill
The PDF Best Practices skill is available as a standalone package that AI assistants can reference.
Install as an agent skill:
```bash theme={null}
npx skills add pdfnoodle/pdf-best-practices
```
This makes the skill available to AI coding assistants that support the skills protocol.
Install via npm for programmatic access:
```bash theme={null}
npm install pdf-best-practices
```
AI assistants can reference the skill directly from GitHub:
```
https://github.com/pdfnoodle/pdf-best-practices
```
The `SKILL.md` file serves as the entry point with links to detailed guides.
***
## What the Skill Covers
The skill includes 8 comprehensive guides:
HTML structure, base CSS, A4 specifications, and typography defaults.
Control where content splits with `page-break-inside`, `break-after`, and orphan/widow handling.
Proper `thead`/`tbody` structure, header repetition, column widths, and zebra striping.
Explicit dimensions, `object-fit`, figures with captions, and image galleries.
Avoid sparse pages, flexible spacing, and content reflow strategies.
The critical `-webkit-print-color-adjust: exact` property and contrast guidelines.
Document headers, page numbers via `footerTemplate`, and letterhead patterns.
Specific guidelines for invoices, reports, certificates, letters, and more.
***
## Default Configuration
The skill recommends these PDF parameters:
```json theme={null}
{
"format": "A4",
"margin": {
"top": "40px",
"right": "40px",
"bottom": "40px",
"left": "40px"
},
"printBackground": true
}
```
### A4 Paper Specifications
| Property | Value |
| ------------------- | --------------------------- |
| Width | 210mm (794px at 96 DPI) |
| Height | 297mm (1123px at 96 DPI) |
| Safe content width | \~714px (with 40px margins) |
| Safe content height | \~1043px per page |
***
## Essential CSS Rules
Every HTML-to-PDF document should include these CSS rules:
```css theme={null}
@page {
size: A4;
margin: 40px;
}
* {
box-sizing: border-box;
}
html, body {
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;
font-size: 12pt;
line-height: 1.5;
color: #333;
}
body {
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
```
The `-webkit-print-color-adjust: exact` property is **critical**. Without it, background colors may not appear in the generated PDF.
***
## Page Break Control
Prevent awkward content splits with these CSS properties:
```css theme={null}
/* Prevent breaks inside elements */
.section, .card, figure, tr {
page-break-inside: avoid;
break-inside: avoid;
}
/* Keep headings with following content */
h1, h2, h3 {
page-break-after: avoid;
break-after: avoid;
}
/* Orphan/widow control for paragraphs */
p {
orphans: 3;
widows: 3;
}
```
### When to Use Page Breaks
| Use `page-break-inside: avoid` on | Use `page-break-before: always` on |
| --------------------------------- | ---------------------------------- |
| Cards and content boxes | Chapter starts |
| Table rows | Major sections |
| Figures with captions | New document parts |
| List items with multiple lines | Title pages |
***
## Table Formatting
Tables require special attention for multi-page documents:
```html theme={null}
Column 1
Column 2
Data 1
Data 2
```
```css theme={null}
table {
width: 100%;
border-collapse: collapse;
font-size: 11pt;
table-layout: fixed;
}
th, td {
border: 1px solid #ddd;
padding: 8px 12px;
text-align: left;
}
th {
background-color: #f5f5f5;
}
tr {
page-break-inside: avoid;
}
```
Always use `` and `` structure. This allows PDF engines to repeat headers on each page when tables span multiple pages.
***
## Image Handling
Always specify explicit dimensions for images:
```html theme={null}
Figure 1: Image caption
```
```css theme={null}
img {
max-width: 100%;
height: auto;
}
figure {
page-break-inside: avoid;
margin: 20px 0;
text-align: center;
}
figcaption {
margin-top: 8px;
font-size: 10pt;
color: #666;
}
```
### Image Guidelines
* **Use absolute URLs** (https\://...) for all images
* **Set max-height** to 300-400px to fit well on pages
* **Group images with captions** using `
`
* **Use `object-fit: contain`** to preserve aspect ratios
***
## Content Density
Avoid pages with minimal content by:
1. **Estimating content height** before structuring documents
2. **Grouping related content** to flow naturally
3. **Using flexible spacing** instead of fixed large gaps
4. **Reducing margins** slightly if the last page is sparse
### Reflow Strategies
If the last page has less than 25% content:
* Reduce margins: 35px instead of 40px
* Tighten line-height: 1.4 instead of 1.5
* Reduce section margins
* Use slightly smaller font for dense data (11pt)
***
## Document Type Guidelines
The skill includes specific recommendations for common document types:
* Keep entire invoice on one page if possible
* Right-align all monetary values
* Use clear table for line items
* Include totals section that stays with items table
* Add payment terms prominently
* Use title page for documents over 3 pages
* Include table of contents
* Page breaks before major sections
* Consistent heading hierarchy
* Executive summary at start
* Center all content
* Larger fonts (18-36pt)
* Decorative borders
* Single page only
* Consider landscape orientation
* Standard business letter format
* Letterhead with logo
* Clear date and recipient block
* Signature area at bottom
* 1-2 pages maximum
***
## Quick Checklist
Before generating any PDF:
* Complete HTML structure (DOCTYPE, html, head, body)
* `@page` rule with A4 size
* Body has `-webkit-print-color-adjust: exact`
* All images have explicit width/height
* Tables use `thead`/`tbody` structure
* `page-break-inside: avoid` on content blocks
* Headings have `page-break-after: avoid`
* No sparse pages (\< 25% content)
* Font sizes 9pt or larger
* pdfParams includes `printBackground: true`
***
## Complete Starter Template
Use this template as a starting point for custom HTML-to-PDF documents:
```html theme={null}
Document Title
Section 1
Content goes here...
Section 2
Column 1
Column 2
Data 1
Data 2
```
***
## Resources
Full skill source with all guides
Install via npm
***
## Next Steps
See the skill in action
All MCP tools documentation
# Examples
Source: https://docs.pdfnoodle.com/integrations/mcp/examples
Real-world prompts and use cases for pdf noodle MCP
This page showcases practical examples of using pdf noodle MCP with AI assistants. Each example includes the prompt you'd use and what to expect.
These examples work with any supported AI assistant. Just copy the prompt and adapt it to your needs.
***
## Getting Started
### Discover Your Templates
Before generating PDFs, see what templates you have:
```text Prompt theme={null}
Show me all my PDF templates
```
```text Expected Response theme={null}
You have 3 templates:
1. **Professional Invoice** (ID: tmp_inv_001)
2. **Meeting Notes** (ID: tmp_notes_002)
3. **Certificate of Completion** (ID: tmp_cert_003)
Would you like to generate a PDF from any of these?
```
### Understand Template Requirements
Check what data a template needs:
```text Prompt theme={null}
What variables does my Professional Invoice template need?
```
```text Expected Response theme={null}
The Professional Invoice template requires:
**Required fields:**
- `invoiceNumber` (string): Unique invoice identifier
- `clientName` (string): Customer's full name
- `clientAddress` (string): Billing address
- `items` (array): Line items with description, quantity, unitPrice
- `dueDate` (string): Payment due date
**Optional fields:**
- `notes` (string): Additional notes or terms
- `taxRate` (number): Tax percentage (default: 0)
```
***
## Invoice Generation
### Basic Invoice
```text Prompt theme={null}
Generate an invoice for:
- Client: Acme Corporation, 456 Business Ave, New York, NY
- Invoice #: INV-2025-042
- Items:
* Website Redesign - $3,500
* SEO Optimization - $1,200
* Monthly Hosting (3 months) - $450
- Due: March 15, 2025
- Add 8% sales tax
```
```text Expected Response theme={null}
✅ Invoice generated successfully!
📄 **Download:** [INV-2025-042.pdf](https://pdfnoodle.com/...)
**Summary:**
- Subtotal: $5,150.00
- Tax (8%): $412.00
- **Total: $5,562.00**
The invoice is ready to send to Acme Corporation.
```
### Multi-Currency Invoice
```text Prompt theme={null}
Create an invoice in EUR for:
- Client: TechStart GmbH, Berlin, Germany
- Services: API Integration (€2,400), Training (€800)
- Include VAT at 19%
- Payment terms: Net 30
```
```text Expected Response theme={null}
✅ Invoice created!
📄 **Download:** [Invoice-TechStart.pdf](https://pdfnoodle.com/...)
**Breakdown:**
- Subtotal: €3,200.00
- VAT (19%): €608.00
- **Total: €3,808.00**
- Due: 30 days from invoice date
```
***
## Reports & Documents
### Sales Report
```text Prompt theme={null}
Using /pdf-best-practices, create a Q4 2024 sales report PDF with:
**Executive Summary:**
Total revenue: $1.2M (up 23% YoY)
Top product: Enterprise Plan (45% of revenue)
New customers: 127
**Monthly Breakdown:**
- October: $380K
- November: $410K
- December: $410K
**Top 5 Customers by Revenue:**
1. GlobalTech Inc - $89K
2. Innovate Labs - $72K
3. DataFlow Corp - $65K
4. CloudFirst - $58K
5. TechVentures - $51K
Use a professional layout with charts if possible.
```
```text Expected Response theme={null}
✅ Sales report generated!
📄 **Download:** [Q4-2024-Sales-Report.pdf](https://pdfnoodle.com/...)
The report includes:
- Executive summary with key metrics
- Monthly revenue chart
- Top customers table
- Year-over-year comparison
Total: 3 pages with professional formatting.
```
### Meeting Minutes
```text Prompt theme={null}
Using /pdf-best-practices, generate meeting minutes PDF:
**Meeting:** Product Roadmap Review
**Date:** January 22, 2025
**Attendees:** Sarah Chen, Mike Torres, Lisa Park, James Wilson
**Agenda Items:**
1. Q1 Feature Priorities
- Decision: Focus on mobile app improvements
- Owner: Mike Torres
- Deadline: March 1
2. Customer Feedback Review
- Key insight: Users want better reporting
- Action: Schedule UX research sessions
- Owner: Lisa Park
3. Technical Debt
- Agreed to allocate 20% of sprint capacity
- Owner: James Wilson
**Next Meeting:** February 5, 2025
```
```text Expected Response theme={null}
✅ Meeting minutes created!
📄 **Download:** [Product-Roadmap-Review-Minutes.pdf](https://pdfnoodle.com/...)
Formatted with:
- Clear header with meeting details
- Numbered agenda items with decisions
- Action items highlighted with owners
- Professional layout
```
***
## Certificates
### Course Completion Certificate
```text Prompt theme={null}
Using /pdf-best-practices, create a certificate for Maria Santos who completed
"Advanced Data Science with Python" on January 20, 2025.
Instructor: Dr. James Mitchell
Certificate ID: CERT-2025-1847
Make it elegant with a gold border.
```
```text Expected Response theme={null}
✅ Certificate generated!
📄 **Download:** [Certificate-Maria-Santos.pdf](https://pdfnoodle.com/...)
The certificate features:
- Elegant design with decorative gold border
- Recipient name in script font
- Course title and completion date
- Instructor signature line
- Unique certificate ID for verification
```
### Achievement Award
```text Prompt theme={null}
Using /pdf-best-practices, generate an Employee of the Month certificate for:
- Name: Alex Thompson
- Month: January 2025
- Reason: Outstanding customer service and team leadership
- Signed by: Jennifer Walsh, VP of Operations
```
```text Expected Response theme={null}
✅ Award certificate created!
📄 **Download:** [Employee-Award-Alex-Thompson.pdf](https://pdfnoodle.com/...)
```
***
## Custom HTML Documents
When creating custom HTML documents, always use `/pdf-best-practices` to ensure proper page breaks, margins, table formatting, and print-optimized CSS.
### Styled Letter
```text Prompt theme={null}
Using /pdf-best-practices, create a professional business letter PDF:
FROM: CloudSync Technologies
123 Innovation Drive, San Francisco, CA 94102
TO: Mr. Robert Chen
Director of IT
Pacific Healthcare Systems
789 Medical Center Blvd
Los Angeles, CA 90045
DATE: January 25, 2025
RE: Partnership Proposal
Dear Mr. Chen,
[First paragraph about the partnership opportunity]
[Second paragraph about benefits]
[Closing paragraph with call to action]
Sincerely,
Amanda Foster
VP of Business Development
CloudSync Technologies
Include company letterhead styling with blue accents.
```
```text Expected Response theme={null}
✅ Business letter generated!
📄 **Download:** [Partnership-Proposal-Letter.pdf](https://pdfnoodle.com/...)
Formatted with:
- Professional letterhead
- Proper business letter spacing
- Blue accent colors as requested
- A4 size with standard margins
```
### Data Table Document
```text Prompt theme={null}
Using /pdf-best-practices, create a PDF with a formatted table of our product inventory:
| SKU | Product Name | Category | Stock | Price |
|-----|--------------|----------|-------|-------|
| SKU001 | Wireless Mouse | Electronics | 145 | $29.99 |
| SKU002 | USB-C Hub | Electronics | 89 | $49.99 |
| SKU003 | Laptop Stand | Accessories | 234 | $79.99 |
| SKU004 | Mechanical Keyboard | Electronics | 56 | $129.99 |
| SKU005 | Monitor Light | Accessories | 178 | $39.99 |
Add alternating row colors and a summary showing:
- Total products: 5
- Total stock value: calculated
- Last updated: January 25, 2025
```
```text Expected Response theme={null}
✅ Inventory report created!
📄 **Download:** [Product-Inventory.pdf](https://pdfnoodle.com/...)
The PDF includes:
- Formatted table with zebra striping
- Currency formatting in Price column
- Summary section with totals
- Total stock value: $25,847.22
```
***
## Template Creation
### Create an Invoice Template
```text Prompt theme={null}
Create a new invoice template called "Modern Invoice" with:
Design:
- Clean, minimal design
- Dark blue header bar with white text
- Company logo placeholder (top left)
- Invoice number and date (top right)
Sections:
- Bill To: Company name, contact name, address, email
- Line items table: Description, Quantity, Unit Price, Amount
- Subtotal, discount field, tax, and total
- Payment information box at the bottom
- Notes section for terms
Make the line items table support up to 15 rows with proper pagination.
```
```text Expected Response theme={null}
✅ Template creation started!
**Template ID:** tmp_modern_inv_abc123
**Status:** Creating...
Template creation typically takes 1-2 minutes. I'll let you know when it's ready, or you can check with:
"Check the status of template tmp_modern_inv_abc123"
```
### Create from Reference
```text Prompt theme={null}
Create a proposal template based on this example:
https://example.com/sample-proposal.pdf
It should have editable fields for:
- Client company name
- Project description
- Deliverables list
- Timeline
- Pricing table
- Terms and conditions
```
```text Expected Response theme={null}
✅ Template creation started!
**Template ID:** tmp_proposal_xyz789
I'm analyzing the reference PDF and creating a template with:
- Matching layout and styling
- All specified editable fields
- Proper section structure
Check back in about 2 minutes for completion.
```
***
## Batch Operations
### Multiple Certificates
```text Prompt theme={null}
Generate completion certificates for these participants:
1. Emma Johnson - Completed January 15
2. Michael Brown - Completed January 16
3. Sofia Garcia - Completed January 17
4. David Kim - Completed January 18
5. Rachel Adams - Completed January 19
Course: "Leadership Excellence Program"
Instructor: Dr. Patricia Moore
```
```text Expected Response theme={null}
✅ Generated 5 certificates!
📄 Downloads:
1. [Emma-Johnson-Certificate.pdf](https://pdfnoodle.com/...)
2. [Michael-Brown-Certificate.pdf](https://pdfnoodle.com/...)
3. [Sofia-Garcia-Certificate.pdf](https://pdfnoodle.com/...)
4. [David-Kim-Certificate.pdf](https://pdfnoodle.com/...)
5. [Rachel-Adams-Certificate.pdf](https://pdfnoodle.com/...)
All certificates use the same template with individual names and dates.
```
***
## Real-World Workflows
### E-Commerce Order Confirmation
```text Prompt theme={null}
Using /pdf-best-practices, generate an order confirmation PDF for:
Order #: ORD-98765
Customer: Jennifer Walsh, jennifer@email.com
Shipping: 742 Evergreen Terrace, Springfield, IL 62701
Items:
- Organic Coffee Beans (2 lbs) × 2 = $34.00
- Ceramic Pour-Over Set × 1 = $45.00
- Coffee Grinder × 1 = $89.00
Subtotal: $168.00
Shipping: $12.00
Tax: $14.40
Total: $194.40
Expected delivery: January 30-31, 2025
```
```text Expected Response theme={null}
✅ Order confirmation generated!
📄 **Download:** [Order-98765-Confirmation.pdf](https://pdfnoodle.com/...)
Ready to email to the customer with:
- Order summary
- Itemized list
- Shipping details
- Delivery estimate
```
### Contract Summary
```text Prompt theme={null}
Using /pdf-best-practices, create a one-page contract summary PDF:
**Agreement:** Software Development Services
**Between:** TechBuild Inc. (Provider) and StartupXYZ (Client)
**Effective Date:** February 1, 2025
**Duration:** 6 months
**Key Terms:**
- Total contract value: $180,000
- Payment schedule: Monthly ($30,000/month)
- Deliverables: Mobile app (iOS + Android)
- Milestones: Design (Month 1), Beta (Month 3), Launch (Month 6)
**Important Clauses:**
- IP rights transfer upon final payment
- 30-day termination notice required
- Confidentiality: 2 years post-contract
Include signature lines for both parties.
```
```text Expected Response theme={null}
✅ Contract summary created!
📄 **Download:** [Contract-Summary-TechBuild-StartupXYZ.pdf](https://pdfnoodle.com/...)
The summary includes:
- Clear party identification
- Key financial terms
- Milestone timeline
- Important clause highlights
- Signature blocks for both parties
```
***
## Tips for Better Results
Include exact names, numbers, and dates. More detail = better PDFs.
Mention sections, columns, and visual preferences explicitly.
State if you want A4, Letter, landscape, or specific margins.
Ask for page numbers, headers, footers, or specific styling.
***
## Next Steps
Learn HTML-to-PDF formatting guidelines
Detailed tool documentation
# MCP Integration
Source: https://docs.pdfnoodle.com/integrations/mcp/index
Connect AI assistants to pdf noodle using the Model Context Protocol
The Model Context Protocol (MCP) is an open standard that enables AI assistants to interact with external services. With pdf noodle's MCP integration, you can generate professional PDFs directly from conversations with AI tools like Claude, Cursor, and ChatGPT.
```
AI Assistant ───────▶ MCP Server ───────▶ pdf noodle API
(Claude, Cursor, (Local or (PDF Generation)
ChatGPT, etc.) Remote)
```
The MCP server acts as a bridge between your AI assistant and the pdf noodle API, enabling natural language PDF generation.
## Why Use MCP?
Generate PDFs by simply describing what you need. No code required.
Let AI handle the complexity of HTML formatting and PDF parameters.
Create, list, and use templates directly from your AI assistant.
AI follows PDF best practices automatically for professional output.
## Connection Options
pdf noodle MCP supports two deployment modes to fit your workflow:
Connect to our hosted MCP endpoint. No installation required—just configure your AI assistant with the remote URL.
```
https://mcp.pdfnoodle.com/mcp?api_key=YOUR_API_KEY
```
**Best for:** ChatGPT, Claude.ai (web), quick setup, always up-to-date
Run the MCP server on your machine using Node.js. Your API key stays local.
```bash theme={null}
npx mcp-server-pdfnoodle
```
**Best for:** Claude Desktop, Cursor, offline access, enterprise environments
## Supported AI Assistants
| Assistant | Remote | Local | Recommended |
| --------------- | ------ | ----- | ----------- |
| Claude Desktop | ✅ | ✅ | Local |
| Cursor | ✅ | ✅ | Local |
| Windsurf | ✅ | ✅ | Local |
| ChatGPT | ✅ | ❌ | Remote |
| Claude.ai (Web) | ✅ | ❌ | Remote |
| n8n | ✅ | ❌ | Remote |
## What You Can Do
### Generate PDFs
Use pre-built templates with dynamic data:
* Invoices with line items and totals
* Reports with charts and tables
* Certificates with custom names and dates
* Letters with personalized content
Convert any HTML directly to PDF:
* Custom documents with full CSS control
* Dynamic content generated by AI
* Complex layouts with images and tables
* Multi-page documents with headers/footers
### Manage Templates
* **List templates** in your account
* **View template details** and required variables
* **Create new templates** using AI from natural language descriptions
* **Get template schemas** to understand what data is needed
## Quick Example
Here's how simple it is to generate a PDF with MCP:
```text User Prompt theme={null}
Create an invoice PDF for Acme Corp with the following:
- Invoice number: INV-2025-001
- Client: John Smith, 123 Main St
- Items: Web Development ($2,500), Hosting ($500), Support ($300)
- Due date: March 1, 2025
```
```text AI Response theme={null}
I've created your invoice! Here's the PDF:
📄 Download: https://pdfnoodle.com/pdf/abc123...
The invoice includes:
- Professional header with Acme Corp branding
- Itemized table with all services
- Subtotal, tax calculation, and total
- Payment terms and due date
```
## Next Steps
Configure MCP for your AI assistant
Explore all available MCP tools
Real-world prompts and use cases
Learn the PDF Best Practices skill
## Resources
* [GitHub Repository](https://github.com/pdfnoodle/mcp-server-pdfnoodle) - Source code and issues
* [npm Package](https://www.npmjs.com/package/mcp-server-pdfnoodle) - Install via npm
* [PDF Best Practices](https://github.com/pdfnoodle/pdf-best-practices) - HTML-to-PDF guidelines
* [MCP Protocol Specification](https://modelcontextprotocol.io) - Official MCP documentation
# Setup Guide
Source: https://docs.pdfnoodle.com/integrations/mcp/setup
Step-by-step instructions to configure pdf noodle MCP for your AI assistant
This guide walks you through configuring pdf noodle's MCP server for each supported AI assistant. Choose the setup that matches your environment.
## Prerequisites
Before you begin, you'll need:
[Create an account](https://app.pdfnoodle.com/auth/sign-up) if you don't have one
Get your API key from [API Settings](https://app.pdfnoodle.com/settings/api)
Have one of the supported AI assistants installed and ready
***
## Claude Desktop
Claude Desktop supports both local and remote MCP servers.
The local server keeps your API key on your machine and works offline.
**Step 1:** Locate your Claude Desktop configuration file:
```text macOS theme={null}
~/Library/Application Support/Claude/claude_desktop_config.json
```
```text Windows theme={null}
%APPDATA%\Claude\claude_desktop_config.json
```
```text Linux theme={null}
~/.config/Claude/claude_desktop_config.json
```
**Step 2:** Add the pdf noodle MCP server configuration:
```json theme={null}
{
"mcpServers": {
"pdfnoodle": {
"command": "npx",
"args": ["-y", "mcp-server-pdfnoodle"],
"env": {
"PDFNOODLE_API_KEY": "your-api-key-here"
}
}
}
}
```
**Step 3:** Restart Claude Desktop
The `-y` flag automatically confirms the npx prompt. The server will be downloaded on first use.
Connect to the hosted MCP endpoint without installing anything locally.
**Step 1:** Open your Claude Desktop configuration file (same paths as above)
**Step 2:** Add the remote server configuration:
```json theme={null}
{
"mcpServers": {
"pdfnoodle": {
"url": "https://mcp.pdfnoodle.com/mcp?api_key=your-api-key-here"
}
}
}
```
**Step 3:** Restart Claude Desktop
With remote mode, your API key is included in the URL. Use this only on trusted networks.
***
## Cursor
Cursor IDE works great with the local MCP server for secure, offline-capable PDF generation.
**Step 1:** Open or create the MCP configuration file:
```text theme={null}
~/.cursor/mcp.json
```
**Step 2:** Add the pdf noodle configuration:
```json theme={null}
{
"mcpServers": {
"pdfnoodle": {
"command": "npx",
"args": ["-y", "mcp-server-pdfnoodle"],
"env": {
"PDFNOODLE_API_KEY": "your-api-key-here"
}
}
}
}
```
**Step 3:** Restart Cursor or reload the window
In Cursor, you can ask the AI to generate PDFs while coding. Try: "Generate a PDF report of this code documentation"
**Step 1:** Open `~/.cursor/mcp.json`
**Step 2:** Add the remote server configuration:
```json theme={null}
{
"mcpServers": {
"pdfnoodle": {
"url": "https://mcp.pdfnoodle.com/mcp?api_key=your-api-key-here"
}
}
}
```
**Step 3:** Restart Cursor
With remote mode, your API key is included in the URL. Use this only on trusted networks.
***
## ChatGPT
ChatGPT only supports remote MCP servers through its Connected Apps feature.
Click your profile icon → Settings
Go to **Connected Apps** or **MCP Servers** section
Click "Add Server" or "Connect App"
* **Name:** pdf noodle
* **URL:** `https://mcp.pdfnoodle.com/mcp?api_key=your-api-key-here`
* **Authentication:** None (API key is in the URL)
Save the configuration and try: "List my PDF templates"
ChatGPT's MCP support may vary by subscription tier and region. Check OpenAI's documentation for the latest availability.
***
## Claude.ai (Web)
Use MCP with Claude directly in your browser.
Click your profile → Settings
Navigate to **Developer** → **MCP Servers**
* **Server URL:** `https://mcp.pdfnoodle.com/mcp?api_key=your-api-key-here`
* **Name:** pdf noodle
Toggle the server on and start a new conversation
***
## Windsurf
Windsurf supports MCP through its configuration system. The local server is recommended for better security and offline access.
**Step 1:** Open or create the Windsurf MCP configuration file (refer to Windsurf's documentation for the exact location)
**Step 2:** Add the pdf noodle configuration:
```json theme={null}
{
"mcpServers": {
"pdfnoodle": {
"command": "npx",
"args": ["-y", "mcp-server-pdfnoodle"],
"env": {
"PDFNOODLE_API_KEY": "your-api-key-here"
}
}
}
}
```
**Step 3:** Restart Windsurf
**Step 1:** Open your Windsurf MCP configuration file
**Step 2:** Add the remote server configuration:
```json theme={null}
{
"mcpServers": {
"pdfnoodle": {
"url": "https://mcp.pdfnoodle.com/mcp?api_key=your-api-key-here"
}
}
}
```
**Step 3:** Restart Windsurf
With remote mode, your API key is included in the URL. Use this only on trusted networks.
***
## n8n
Integrate pdf noodle MCP into automated workflows using n8n. n8n connects to the remote MCP server via HTTP requests.
Open n8n and create a new workflow or edit an existing one
Click the **+** button and search for "HTTP Request"
Set up the HTTP Request node with these settings:
| Setting | Value |
| ------------------ | --------------------------------------------------------- |
| **Method** | `POST` |
| **URL** | `https://mcp.pdfnoodle.com/mcp?api_key=your-api-key-here` |
| **Authentication** | `None` |
| **Content-Type** | `application/json` |
Authentication is set to **None** because the API key is passed directly in the URL query parameter.
Switch to the **Body** tab and select **JSON**. Use the JSON-RPC 2.0 format:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "html_to_pdf",
"arguments": {
"html": "
Hello from n8n!
",
"pdfParams": "{\"format\": \"A4\"}"
}
}
}
```
Click **Test step** to run the request. You should receive a response with the PDF download URL.
### Available MCP Methods for n8n
| Method | Description | Example params.name |
| ------------ | -------------------- | ----------------------------------------------- |
| `tools/call` | Execute a tool | `html_to_pdf`, `generate_pdf`, `list_templates` |
| `tools/list` | List available tools | — |
### Example: Generate PDF from Template
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "generate_pdf",
"arguments": {
"templateId": "your-template-id",
"data": "{\"customerName\": \"John Doe\", \"invoiceNumber\": \"INV-001\"}"
}
}
}
```
### Example: List All Templates
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "list_templates",
"arguments": {}
}
}
```
For production workflows, store your API key securely using n8n credentials or environment variables instead of hardcoding it in the URL.
You can chain multiple n8n nodes together to create complex workflows—for example, fetch data from a database, generate a PDF, and send it via email.
***
## Manual Execution
Run the MCP server directly from the command line for testing or debugging.
```bash theme={null}
# Set your API key
export PDFNOODLE_API_KEY=your-api-key-here
# Run the server
npx mcp-server-pdfnoodle
```
The server will start and listen for MCP protocol messages on stdin/stdout.
***
## Verifying Your Setup
After configuration, verify everything works:
Open your AI assistant and start a fresh conversation
Ask: **"List my pdf noodle templates"**
You should see a list of templates or a message that no templates exist yet
If you encounter issues, check the [Troubleshooting](#troubleshooting) section below.
***
## Troubleshooting
* Verify your API key is correct
* Check that the configuration file syntax is valid JSON
* Ensure you've restarted the AI assistant after configuration changes
* For local servers, verify Node.js is installed (`node --version`)
* Double-check your API key at [app.pdfnoodle.com/settings/api](https://app.pdfnoodle.com/settings/api)
* Ensure there are no extra spaces in the API key
* Verify your account is active and has available credits
* Some AI assistants cache tool lists—try restarting completely
* Check if MCP is enabled in your assistant's settings
* Verify the server name matches in your configuration
* Check your account has sufficient credits
* For HTML-to-PDF, ensure the HTML is valid
* Review the [PDF Best Practices](/integrations/mcp/best-practices) for formatting guidelines
***
## Security Considerations
* Never commit API keys to version control
* Use environment variables when possible
* Rotate keys periodically
* Remote URLs include your API key—use HTTPS only
* Prefer local servers for sensitive environments
* Review your assistant's data handling policies
***
## Next Steps
Explore all available MCP tools
See real-world prompts and use cases
# Tools Reference
Source: https://docs.pdfnoodle.com/integrations/mcp/tools
Complete reference for all pdf noodle MCP tools
pdf noodle MCP provides 7 tools organized into two categories: **PDF Generation** and **Template Management**. This reference documents each tool's parameters, behavior, and usage examples.
You don't need to memorize tool names or parameters. Simply describe what you want in natural language, and the AI will select the appropriate tool automatically.
## Tool Summary
| Tool | Purpose | Key Parameters |
| ------------------------- | ------------------------ | ------------------- |
| `generate_pdf` | Create PDF from template | templateId, data |
| `html_to_pdf` | Convert HTML to PDF | html, pdfParams |
| `check_pdf_status` | Check async PDF status | requestId |
| `list_templates` | List all templates | — |
| `get_template` | Get template details | templateId |
| `get_template_schema` | Get template variables | templateId |
| `create_template_with_ai` | Create template with AI | displayName, prompt |
***
## PDF Generation Tools
These tools create PDF documents from templates or raw HTML.
### generate\_pdf
Generate a PDF document using a saved template and dynamic data.
The unique identifier of the template to use. Get this from `list_templates` or the pdf noodle dashboard.
Object containing the template variables. Must match the schema returned by `get_template_schema`. Example: `{"customerName": "John Doe", "invoiceNumber": "INV-001", "items": [...]}`
Return a PNG image instead of PDF. Useful for previews or social media.
PDF metadata object. Example: `{"title": "Invoice", "author": "Company Name", "subject": "Invoice #123", "keywords": "invoice, billing"}`
Hide header and footer on the first page. Useful for title pages.
URL expiration time in seconds. Default is 1 hour (3600 seconds).
S3 bucket ID for storing the output file. Requires S3 integration. If provided, `s3_key` must also be specified.
S3 path/key for the output file (e.g., "invoices/2024/inv-001"). Only used when `s3_bucket` is provided.
**Example Prompts:**
```text theme={null}
Generate an invoice using template inv-2024 with:
- Customer: John Smith
- Items: Consulting ($500), Development ($1200)
- Invoice number: INV-001
```
```text theme={null}
Create a PDF certificate from my certificate template for
"Maria Garcia" who completed "Advanced Python Course" on January 15, 2025
```
***
### html\_to\_pdf
Convert HTML content directly to a PDF document. This tool provides full control over the document structure and styling.
For best results with `html_to_pdf`, follow the [PDF Best Practices](/integrations/mcp/best-practices) guidelines. Poorly structured HTML can result in broken layouts and awkward page breaks.
Complete HTML content to render as PDF. Should include DOCTYPE, html, head, and body tags with proper CSS for print.
PDF formatting options object. Properties:
* `format` (string): Paper size - "A4", "Letter", "Legal", etc.
* `margin` (object): Page margins with `top`, `right`, `bottom`, `left` (e.g., `{"top": "40px", "right": "40px", "bottom": "40px", "left": "40px"}`)
* `printBackground` (boolean): Include CSS backgrounds and colors
* `landscape` (boolean): Landscape orientation
* `displayHeaderFooter` (boolean): Show header/footer
* `headerTemplate` (string): HTML template for page headers
* `footerTemplate` (string): HTML template for page footers
Example: `{"format": "A4", "margin": {"top": "40px", "right": "40px", "bottom": "40px", "left": "40px"}, "printBackground": true}`
Return a PNG image instead of PDF.
PDF metadata object. Example: `{"title": "Report", "author": "Author Name", "subject": "Subject", "keywords": "keyword1, keyword2"}`
Hide header/footer on the first page.
URL expiration time in seconds. Default is 1 hour (3600 seconds).
S3 bucket ID for storage. Requires S3 integration. If provided, `s3_key` must also be specified.
S3 path/key for the output file (e.g., "reports/2024/report.pdf"). Only used when `s3_bucket` is provided.
**Recommended PDF Parameters:**
```json theme={null}
{
"format": "A4",
"margin": {
"top": "40px",
"right": "40px",
"bottom": "40px",
"left": "40px"
},
"printBackground": true
}
```
**Example Prompts:**
```text theme={null}
Convert this markdown report to a PDF with proper formatting,
A4 size, and page numbers in the footer
```
```text theme={null}
Create a PDF from HTML with a table of our Q4 sales data.
Include a header with our logo and footer with page numbers.
```
**Using the PDF Best Practices Skill:**
For complex documents, instruct the AI to use the pdf-best-practices skill first:
```text theme={null}
Using /pdf-best-practices, generate a professional invoice PDF with:
- Company: TechCorp Inc.
- Client: Jane Smith
- Items: Consulting ($2,000), Development ($5,000)
- Include proper page breaks, table formatting, and print-optimized CSS
```
```text theme={null}
Following the pdf-best-practices guidelines, create a multi-page report PDF
from this data. Make sure tables don't break across pages and headings
stay with their content.
```
When you reference `/pdf-best-practices` or ask the AI to follow the pdf-best-practices skill, the AI will automatically apply proper CSS rules for page breaks, margins, table formatting, and print-optimized styling.
***
### check\_pdf\_status
Check the status of an asynchronous PDF generation request.
The request ID returned from a queued PDF generation.
**Response States:**
| Status | Meaning |
| --------- | ------------------------------------- |
| `SUCCESS` | PDF is ready, download URL provided |
| `ONGOING` | Still processing, check again shortly |
| `FAILED` | Generation failed, try again |
**Example Prompt:**
```text theme={null}
Check the status of PDF request abc123
```
Most PDF generations complete synchronously. This tool is only needed for large or complex documents that are queued for background processing.
***
## Template Management Tools
These tools help you work with reusable PDF templates.
### list\_templates
Retrieve all PDF templates in your account.
*This tool has no parameters.*
**Returns:** A list of templates with their display names and IDs.
**Example Prompts:**
```text theme={null}
Show me all my PDF templates
```
```text theme={null}
What templates do I have available?
```
***
### get\_template
Fetch details of a specific template including its status.
The unique ID of the template to retrieve.
**Returns:** Template name, ID, and status (COMPLETED, ONGOING, or FAILED).
**Status Values:**
| Status | Meaning |
| ----------- | ------------------------------- |
| `COMPLETED` | Template is ready to use |
| `ONGOING` | Template is still being created |
| `FAILED` | Template creation failed |
The status field is only relevant when you've created a template using `create_template_with_ai` via MCP or the API. Template creation typically takes 1-2 minutes. Use this tool to check if your new template is ready.
**Example Prompts:**
```text theme={null}
Get details for template tmp_abc123
```
```text theme={null}
Check if my new invoice template is ready yet
```
***
### get\_template\_schema
Retrieve the variables and schema required by a template.
The ID of the template to get the schema for.
**Returns:** Object describing all template variables and their structure. The schema shows the exact data structure needed for the `data` parameter in `generate_pdf`.
**Example Prompts:**
```text theme={null}
What variables does my invoice template need?
```
```text theme={null}
Show me the schema for template tmp_invoice_v2
```
Use this tool before `generate_pdf` to understand exactly what data a template expects. The AI will format your data correctly based on the schema.
***
### create\_template\_with\_ai
Create a new reusable PDF template using AI from a natural language description.
Human-readable name for the template (e.g., "Invoice Template", "Meeting Notes").
Detailed description of the template design, layout, and fields. Be specific about:
* Document type and purpose
* Sections and their content
* Data fields that should be variable
* Styling preferences (colors, fonts)
* Any images or logos to include
Optional URL to a reference PDF or image for design inspiration. The AI will use this as a reference when creating the template.
**Example Prompts:**
```text theme={null}
Create a professional invoice template with:
- Company logo placeholder at the top
- Invoice number, date, and due date fields
- Bill-to section with customer details
- Line items table with description, quantity, unit price, total
- Subtotal, tax, and grand total section
- Payment terms at the bottom
- Modern design with blue accent colors
```
```text theme={null}
Create a certificate template based on this example: https://example.com/cert.pdf
It should have fields for recipient name, course title, completion date, and instructor signature
```
Template creation typically takes 1-2 minutes. Use `get_template` with the returned ID to check when it's ready.
***
## Best Practices
Describe what you want instead of specifying exact parameters. The AI handles the technical details.
Use `get_template_schema` before generating PDFs to ensure you provide all required data.
For `html_to_pdf`, review the [Best Practices](/integrations/mcp/best-practices) to avoid formatting issues.
More detail in your prompts leads to better results, especially for template creation.
***
## Next Steps
See these tools in action with real prompts
Learn HTML-to-PDF formatting guidelines
# n8n Integration
Source: https://docs.pdfnoodle.com/integrations/n8n/index
Automate PDF generation workflows by connecting n8n with pdf noodle
Integrate pdf noodle with [n8n](https://n8n.io) to automate your PDF generation workflows. Create invoices, reports, certificates, and any other documents automatically—no code required.
## Prerequisites
Before you begin, make sure you have:
* A pdf noodle account ([sign up here](https://app.pdfnoodle.com/auth/sign-up))
* Your API key from [API Settings](https://app.pdfnoodle.com/settings/api)
* At least one template created in pdf noodle
* n8n installed ([cloud](https://n8n.io) or [self-hosted](https://docs.n8n.io/hosting/))
***
## Setting Up the Integration
n8n connects to pdf noodle using the **HTTP Request node**.
In n8n, create a new workflow and add your trigger (e.g., Webhook, Schedule, or any app trigger).
Click the **+** button and search for **HTTP Request**.
Set up the HTTP Request node:
| Setting | Value |
| ------------------ | -------------------------------------------- |
| **Method** | `POST` |
| **URL** | `https://api.pdfnoodle.com/v1/pdf/sync` |
| **Authentication** | `Predefined Credential Type` → `Header Auth` |
Create a Header Auth credential:
| Setting | Value |
| --------- | --------------------- |
| **Name** | `Authorization` |
| **Value** | `Bearer YOUR_API_KEY` |
Select **Body** → **JSON** and add:
```json theme={null}
{
"templateId": "your-template-id",
"data": {
"customerName": "{{ $json.customer.name }}",
"invoiceNumber": "{{ $json.order.id }}",
"total": {{ $json.order.total }}
}
}
```
Use n8n's expression syntax to map data from previous nodes.
***
## Finding Your Template ID
1. Go to [app.pdfnoodle.com](https://app.pdfnoodle.com)
2. Navigate to the **Templates** section
3. Copy the Template ID shown in the list next to your template
***
## Response
When the request succeeds, you'll receive:
```json theme={null}
{
"signedUrl": "https://pdforge-production.s3.us-east-2.amazonaws.com/...",
"metadata": {
"executionTime": "1.805 seconds",
"fileSize": "4.066 kB"
}
}
```
Use the `signedUrl` in subsequent nodes to send via email, upload to storage, or attach to records.
***
## Example Workflow
A typical workflow that generates an invoice when a new order is received:
1. **Webhook Trigger** - Receives order data
2. **HTTP Request** - Generates the PDF with pdf noodle
3. **Send Email** - Sends the PDF to the customer using `{{ $json.signedUrl }}`
***
## Error Handling
* Verify your API key is correct
* Ensure the Authorization header format is `Bearer YOUR_API_KEY`
* Check your JSON syntax is valid
* Ensure all required template variables are provided
* Verify the template ID exists
* Double-check the Template ID
* Ensure the template exists in your pdf noodle account
***
## API Endpoints
| Endpoint | Description |
| --------------------------- | ----------------------------------------- |
| `POST /v1/pdf/sync` | Generate PDF from template (synchronous) |
| `POST /v1/pdf/async` | Generate PDF from template (asynchronous) |
| `POST /v1/html-to-pdf/sync` | Convert HTML to PDF (synchronous) |
See the complete [API Reference](/api-reference) for all endpoints and options.
***
You can also generate PDFs using any HTTP Request node by calling the pdf noodle API directly. See the [API Reference](/api-reference) for all available endpoints and options.
## Resources
* [n8n Integration Page](https://pdfnoodle.com/integrations/n8n) - pdf noodle + n8n overview
* [n8n Automation Guide](https://pdfnoodle.com/blog/how-to-automate-pdf-generation-with-n8n-and-pdforge) - Step-by-step tutorial
* [n8n Documentation](https://docs.n8n.io) - Official n8n docs
* [pdf noodle API Reference](/api-reference) - Full API documentation
# Zapier Integration
Source: https://docs.pdfnoodle.com/integrations/zapier/index
Automate PDF generation workflows by connecting Zapier with pdf noodle
Integrate pdf noodle with [Zapier](https://zapier.com) to automate your PDF generation workflows. Connect to 5000+ apps and generate documents with zero code.
## Prerequisites
Before you begin, make sure you have:
* A pdf noodle account ([sign up here](https://app.pdfnoodle.com/auth/sign-up))
* Your API key from [API Settings](https://app.pdfnoodle.com/settings/api)
* At least one template created in pdf noodle
* A Zapier account ([sign up here](https://zapier.com))
***
## Setting Up the Integration
Zapier connects to pdf noodle using **Webhooks by Zapier** to call the API.
In Zapier, click **Create Zap** and choose your trigger app (e.g., Google Sheets, Typeform, Stripe).
For the action step, search for **Webhooks by Zapier** and select **POST**.
Set up the webhook with these settings:
| Setting | Value |
| ---------------- | --------------------------------------- |
| **URL** | `https://api.pdfnoodle.com/v1/pdf/sync` |
| **Payload Type** | `json` |
| **Data** | See below |
In the **Headers** section, add:
| Key | Value |
| --------------- | --------------------- |
| `Authorization` | `Bearer YOUR_API_KEY` |
| `Content-Type` | `application/json` |
Add the following data fields:
| Key | Value |
| ------------ | ----------------------------------------------------------- |
| `templateId` | `your-template-id` |
| `data` | `{"customerName": "{{customer_name}}", "total": {{total}}}` |
Use Zapier's field picker to map data from your trigger.
Click **Test action** to generate a test PDF. You should see a response with the PDF URL.
***
## Finding Your Template ID
1. Go to [app.pdfnoodle.com](https://app.pdfnoodle.com)
2. Navigate to the **Templates** section
3. Copy the Template ID shown in the list next to your template
***
## Example: Certificate on Course Completion
A typical Zap that generates a certificate when a student completes a course:
1. **Trigger** - Teachable "Course Completed"
2. **Webhooks** - Generate certificate PDF with pdf noodle
3. **Gmail** - Email the certificate to the student
***
## Working with the Response
The webhook returns a JSON response:
```json theme={null}
{
"signedUrl": "https://pdforge-production.s3.us-east-2.amazonaws.com/...",
"metadata": {
"executionTime": "1.805 seconds",
"fileSize": "4.066 kB"
}
}
```
To use the PDF URL in subsequent steps:
1. Add another action after the webhook
2. Use the response field `signedUrl` from the webhook step
***
## Common Use Cases
| Trigger | Action | Result |
| ---------------- | -------------------- | --------------------- |
| Stripe payment | Generate invoice | Email PDF to customer |
| Form submission | Generate contract | Save to Google Drive |
| CRM deal closed | Generate proposal | Attach to record |
| Course completed | Generate certificate | Email to student |
***
## Error Handling
* Verify your API key is correct
* Check the Authorization header format: `Bearer YOUR_API_KEY`
* Ensure the `data` field is valid JSON
* Check that all required template variables are included
* Verify the template ID exists
* Check your Zapier task limits
* Review the error in Zap History
* Verify your pdf noodle plan has available credits
***
## API Endpoints
| Endpoint | Description |
| --------------------------- | ----------------------------------------- |
| `POST /v1/pdf/sync` | Generate PDF from template (synchronous) |
| `POST /v1/pdf/async` | Generate PDF from template (asynchronous) |
| `POST /v1/html-to-pdf/sync` | Convert HTML to PDF (synchronous) |
See the complete [API Reference](/api-reference) for all endpoints and options.
***
You can also generate PDFs using Webhooks by Zapier or any HTTP Request action by calling the pdf noodle API directly. See the [API Reference](/api-reference) for all available endpoints and options.
## Resources
* [Zapier Integration Page](https://pdfnoodle.com/integrations/zapier) - pdf noodle + Zapier overview
* [Zapier Automation Guide](https://pdfnoodle.com/blog/how-to-automate-pdf-generation-with-zapier-and-pdforge) - Step-by-step tutorial
* [Zapier Help Center](https://help.zapier.com) - Official Zapier docs
* [pdf noodle API Reference](/api-reference) - Full API documentation
# Charts
Source: https://docs.pdfnoodle.com/knowledge-base/components/charts
Visualize metrics beautifully and fast. The Chart component lets you add six chart types, bind them to dynamic data, and fine-tune design details. Perfect for KPIs, trends, and comparisons in your PDFs.
***
### Supported chart types
* Bar
* Column
* Area
* Line
* Donut
* Radial (Gauge)
We render charts with [**ApexCharts**](https://apexcharts.com/). For any
capability beyond what’s exposed in the UI, you can rely on ApexCharts options
(see “Options Override” below).
***
### Data & variables (dynamic payload)
Charts will be rendered using dynamic via payload.
Example (Bar chart) – output variable:
```json theme={null}
{
"bar_chart_title": {
"series_a": ["your bar_chart_title.series_a here"],
"labels": ["your bar_chart_title.labels here"]
}
}
```
* series\_a (Y-axis): numbers or strings that can be parsed as numbers.
* labels (X-axis): strings only.
#### Identifier → payload key
We automatically create the payload key from the Identifier (lowercased & snake\_cased).
Examples
* Identifier: Bar chart title → payload key: bar\_chart\_title
* Identifier: Monthly Revenue → payload key: monthly\_revenue
#### Multiple series
Add more series in Data → Add new series. Each series becomes its own array inside the identifier:
```
{
"monthly_revenue": {
"series_a": [1200, 1300, 1500],
"series_b": [900, 1050, 1100],
"labels": ["Jan", "Feb", "Mar"]
}
}
```
For example, if your identifier is Monthly Revenue, send a payload object under monthly\_revenue with series\_\* and labels.
**Series names and titles can't start with a number!** \ \ If you're series is
something like "2025 values", change it to "values - 2025", so the variables
can be properly set.
***
### How to use (step by step)
1. Insert a Chart and choose a type (Bar, Column, Area, Line, Donut, Radial).
2. In General, set:
* Identifier (drives the dynamic payload key).
* Subtitle (supports plain text; see Tips for HTML).
3. In Data, configure each Series:
* Name (legend/series label).
* Color (HEX).
* Label color (for on-bar/on-point labels when enabled).
* Show label (toggle data labels per series).
* Add new series as needed (not applicable to Donut/Radial which are single-series visually).
4. Bind dynamic data by sending the payload described above.
5. Adjust Design (see next section).
6. (Advanced) Use Options override for any ApexCharts option you need to fine-tune.
***
### Design options (UI)
#### Layout
* Margin (Horizontal Margin convention):
* None 0px · Small 12px · Medium 20px · Large 40px
* Height (px)
* Width (auto or px)
* Stacking (Bar/Column): *Don’t stack* or *Stack*
* Hide title: toggle title/subtitle visibility
* Card border: toggle outer border
**Hint: If you're using 2 charts horizontally aligned**, it's recommended to set a width for each chart so they don't take more space than they should.
#### Data labels
* Position: top, center, bottom (varies by chart)
* Orientation: Horizontal or Vertical
* Font size: px
* Format: number format (e.g., 1,234)
* Prefix / Suffix: add currency or units (e.g., \$, %, kg)
#### Y Axis
* Show axis
* Show labels
* Horizontal grid
* Title (text)
* Prefix/Suffix (helpful for currencies or units)
#### X Axis
* Show labels
* Title (text)
***
### Visibility (conditional rendering)
If you don't send the chart variable on the payload, the chart won't be rendered, meaning the chart will be heading.
***
### Options Override (advanced)
Override any ApexCharts option via JSON for extra flexibility.
**How it works**
* We deep-merge your JSON with our defaults.
* Only the specified properties are overridden.
Example
```
{ "xaxis": { "min": 0 }, "yaxis": { "max": 100 } }
```
This sets x-axis min to 0 and y-axis max to 100 without affecting other options.
👉🏻 See all options: **[https://apexcharts.com/docs/options/](https://apexcharts.com/docs/options/)**
**Common overrides you might use**
```
{
"plotOptions": { "bar": { "borderRadius": 6 } },
"dataLabels": { "enabled": true, "offsetY": -6 },
"legend": { "position": "bottom" },
}
```
**Note:** Functions inside JSON are supported only when applicable in our
runtime. Prefer static options when possible.
***
### Payload Examples per Chart
#### 1) Single series (Column)
```
{
"monthly_signups": {
"series_a": [120, 140, 200, 180],
"labels": ["Jan", "Feb", "Mar", "Apr"]
}
}
```
#### 2) Two series (Line)
```
{
"active_vs_new_users": {
"series_a": [320, 340, 310, 360],
"series_b": [120, 160, 150, 190],
"labels": ["Week 1", "Week 2", "Week 3", "Week 4"]
}
}
```
#### 3) Donut (categories)
```
{
"traffic_sources": {
"series_a": [55, 25, 20],
"labels": ["Organic", "Paid", "Referral"]
}
}
```
#### 4) Radial (single value)
```
{
"target_completion": {
"series_a": [76],
"labels": ["Completion"]
}
}
```
**You can’t combine different chart types in a single chart right now** (e.g.,
line + column). It’s on our roadmap. If you’d like this, email
[support@pdfnoodle.com](mailto:support@pdfnoodle.com).
***
### F.A.Q.
* **Nothing renders?** Check you're sending the chart payload properly
* **No data labels showing?** Enable Data labels and/or per-series Show label.
* **Values look wrong?** Ensure series are numbers (or numeric strings). Labels must be strings.
* **Need a very specific tweak?** Use Options override with the corresponding ApexCharts option.
# Container
Source: https://docs.pdfnoodle.com/knowledge-base/components/container
The container component is one of pdf noodle key features, giving you superpowers to make your template look exactly how you want it. It's super flexible, allowing you to craft even complex layouts without ever needing to write a line of code.
### Layout
The layout properties will tell you how the children components will behave inside the container. You can change:
* **Direction:** vertical or horizontal stacking
* **Align:** on top, in the middle or on the bottom
* **Justify:** at the start, at the center, at the end, distribute evenly
* **Gap between elements:** none (0px) , small (12px), medium (20px) or large (40px)
* **Horizontal margin:** none (0px) , small (12px), medium (20px) or large (40px)
* **Padding (internal margin):** none (0px) , small (12px), medium (20px) or large (40px)
### Design
With the design properties, you'll tell us how the container should look. The options are:
* **Height:** fit internal content, screen height (ideal for one-pagers or covers), fill space (ideally if you have nested containers) or fixed height.
* **Width:** Full available width, fit internal content or fixed width.
* **Background:** solid color, gradient or image **(more below)**
* **Border:** none, full, top, bottom, left or right
* Border color (if border is choosen)
### Container background
For the container background, you can choose from a list of solid colors, gradients or images, but it's actually a lot mor flexible than that.
For the solid colors, you can type any HEX/RGB color or even type "transparent" and it will also work.
For the gradient, you can choose one gradient or create your own using the syntax `linear-gradient(...)`.
For the image, you can either upload an image, using the upload button, or insert the url of any image using the syntax `url(...)`.
**Hint:** If you want, you can insert a as a container background, so you send the background color/gradient/image dynamically through a variable on the payload to generate the PDF.
### Stacking containers
The best part? Containers are stackable! Mix and match different layout options to effortlessly build your ideal design. Simple, powerful, and intuitive—just the way it should be.
**Example:**
### Visibility convention
If you want to conditionally render a text component, you can set a variable that controls its visibility. When this variable is included in the variables payload, the component will display; if it’s absent, the component won’t appear in the final render.
# Divider
Source: https://docs.pdfnoodle.com/knowledge-base/components/divider
The Divider component is a simple but effective way to visually separate sections of your document. It helps improve readability by clearly marking boundaries between content blocks in your PDF.
### Styling Options
You can customize the divider’s appearance by choosing one of the following styles:
* Solid – a clean, continuous line.
* Dashed – spaced dashes, for lighter separation.
* Dotted – a dotted line, subtle and minimal.
* Double – two parallel lines for emphasis.
**You can't change the divider color yet**. If that's something that you'd
like, just send us a message at
[support@pdfnoodle.com](mailto:support@pdfnoodle.com) and we'll implement that
for you.
### Conditional Rendering
Although not commonly needed, you can also use the Show if field to conditionally render the Divider. If the variable is present in the payload, the Divider will appear; if not, it will be hidden.
**Example:**
```
{
"show_divider": true
}
```
When using `{{ show_divider }}`, the divider will only render if the variable is passed.
💡**Tip:** Use dividers sparingly to avoid clutter—stick to separating major
content sections for the best visual balance.
# HTML
Source: https://docs.pdfnoodle.com/knowledge-base/components/html
Our HTML component was designed so you have the maximum flexibility to build your own components on the exact way you want. To have a better view on the code editor and the component itself, you can double click on the preview canvas.
To enhance your productivity, we have native integration with [**TailwindCSS**](https://tailwindcss.com).
Let our AI Agent do the heavy lifting for you and create the HTML component
with a simple text prompt. Check out our [**AI Generated
Section**](https://docs.pdforge.com/knowledge-base/components/ai-generated) to
learn more about it.
### Handlebars integration
We use [handlebars](https://handlebarsjs.com/) as our templating engine, so you also have all the flexibility that handlebars delivers inside your html components.Here's some examples:
#### Variables
You can differentiate between dynamic variables and static text in your templates. Here’s how it works:
• Simple Variable: Enclose your text in double curly brackets: `{{ }}`.
• HTML Variable: Enclose your text in triple curly brackets: `{{{ }}}` to include HTML tags.
#### Conditional Rendering
On handlebars, you can use `if`, `else`, `unless` syntax to conditionally render a block of code.
If you want to only render a block when a variable is sent:
```
{{#if variable_name}}
// conditionally rendered html content here
{{/if}}
```
If you want to conditionally render with a fallback:
```
{{#if variable_name}}
// variable_name rendered html content here
{{else}}
// fallback html content here
{{/if}}
```
You can also use the alternative to NOT render a block when a variable is sent:
```
{{#unless variable_name}}
// conditionally rendered html content here if variable not sent
{{/unless}}
```
#### Loops
On handlebars, you can either **loop through arrays or objects**.
Heres how to loop through an **array of strings**:
```
{{#each array_of_strings}}
{{this}}
{{/each}}
```
Heres how to loop through an **array of objects**:
```
{{#each array_of_objects as |object|}}
{{object.id}}
{{object.name}}
{{/each}}
```
**Hint:** When you loop through an array, you have access to this variable `{{@index}}`, which returns the index of the array item.
Heres how to loop through an **object**:
```
{{#each array_of_strings}}
{{this}}
{{/each}}
```
**Hint:** When you loop through an object, you have access to this variable `{{@key}}`, which returns the key of the looped object.
Heres how to loop through an a **nested array of objects**:
Array of Objects Example:
```json theme={null}
[
{
"id": "item-id",
"results": [
{
"id": "result-id",
"name": "result-name"
}
]
]
```
The syntax would be:
```
{{#each nested_array_of_objects as |object|}}
{{object.id}}
{{#each object.results as |result|}}
{{result.id}} - {{result.name}}
{{/each}}
{{/each}}
```
To see more handlebars functions, you can check out their [documentation
here](https://handlebarsjs.com/guide/#evaluation-context).
### How to use Icons on your HTML component
We also have a native integation with [**Lucide Icons**](https://lucide.dev/).
You can use them on your HTML component with the following syntax:
```html theme={null}
```
**You won't be able to see the Icon on the preview canvas yet**, but if you click on "Generate sample", you'll be able to see the icon.
**Hint:** You can set your icon to `data-lucice="{{ icon }}"` to pass the icon
as a variable from the payload.
If you didn’t find what you were looking for regarding the Page Component,
feel free to contact us at
[support@pdfnoodle.com](mailto:support@pdfnoodle.com). We’ll get back to you
as quickly as possible!
# Icon
Source: https://docs.pdfnoodle.com/knowledge-base/components/icon
Our Icon Component lets you easily pick and insert icons from our integrated [**Lucide Icons**](https://lucide.dev/) **library.**
### Icon Size Convention
To keep your templates consistent, we’ve standardized icon sizes:
* **Extra small:** 10px
* **Small:** 12px
* **Medium:** 14px
* **Large:** 16px
* **Extra large:** 20px
### Send icon dynamically
You can also set your icons dynamically by using variables. **We automatically generate the variable name based on your component title**. Just provide the icon name in kebab-case format (e.g., circle-alert) in your PDF payload.
Its mandatory to choose a fallback icon in case you didn't send an icon as a
payload.
# Image
Source: https://docs.pdfnoodle.com/knowledge-base/components/image
The Image Component lets you easily add images to your templates—either dynamically or statically.
### Static Images
To add a static image, simply click **Upload image**, or use an online image by entering its URL in the format: `url(your-image-url)`.
### Dynamic Images
To use dynamic images, enable the Dynamic source option. We’ll automatically create a variable based on your component title, formatted as: `component_title_src`.
### Customizing Your Image
You can further style your images by adjusting:
* **Horizontal Margin:** none (0px), small (12px), medium (20px) or large (20px)
* **Border:** light gray border around the image
* **Rounded:** none (0px), small (6px), medium (8px) or full (9999px)
* **Object fit:** contain, cover, fill, none or scale down
* **Image size:** full with or fixed size (specific height/width)
### Visibility convention
If you choose dynamic images but don’t include the corresponding variable in the payload, the image component won’t appear in the final PDF render.
# Introduction
Source: https://docs.pdfnoodle.com/knowledge-base/components/introduction
One of the most important parts of pdf noodle is our opinionated no-code template builder. With it, you can create stunning reports using our pre-designed components, allowing you to focus less on minor details and more on achieving a polished, professional result.
Our components are categorized into two types:
### Low-Level Components
These are highly flexible and can be customized to suit your needs:
• [Text](https://docs.pdforge.com/knowledge-base/components/text)
• [HTML](https://docs.pdforge.com/knowledge-base/components/html)
• [Markdown](https://docs.pdforge.com/knowledge-base/components/markdown)
• [Container](https://docs.pdforge.com/knowledge-base/components/container)
### Opinionated Components
These require minimal customization and are designed with predefined styles and functionality:
• [Image](https://docs.pdforge.com/knowledge-base/components/image)
• [Icons](https://docs.pdforge.com/knowledge-base/components/icon)
• [Table](https://docs.pdforge.com/knowledge-base/components/table)
• [Metrics Cards](https://docs.pdforge.com/knowledge-base/components/metrics)
• Alert
• Tags
• QR Code
• [Charts](https://docs.pdforge.com/knowledge-base/components/charts)
• [Page Break](https://docs.pdforge.com/knowledge-base/components/page-break)
• [Divider](https://docs.pdforge.com/knowledge-base/components/divider)
• [Loop](https://docs.pdforge.com/knowledge-base/components/loop)
To learn more about a specific component, explore the detailed documentation available in the sidebar of this knowledge base.
### How does variables work?
The variables that will be replaced to generate the final PDF or image will be **generated automatically according to the Title** of each component. You can see specific rules going to the detailed component documentation.
**You can't customize the variable name** from opinionated components, as
**they will be generated automatically** according to the component title and
content.
**On low-level components** you can also generate variables using our variables syntax.
We use [handlebars](https://handlebarsjs.com/) as our templating engine to differentiate between dynamic variables and static text in your templates. Here’s how it works:
• Simple Variable: Enclose your text in double curly brackets: `{{ }}`.
• HTML Variable: Enclose your text in triple curly brackets: `{{{ }}}` to include HTML tags.
For more details on Handlebars syntax, please check out our guide in the
[**HTML component**](https://docs.pdforge.com/knowledge-base/components/html).
We're still working on the documentation of every component, so if you have
any questions regarding a component that you didn't find here, you can message
us at [support@pdfnoodle.com](mailto:support@pdfnoodle.com) and we'll get back
to you really fast.
# Loop
Source: https://docs.pdfnoodle.com/knowledge-base/components/loop
The Loop component lets you easily repeat a layout based on dynamic data.
When creating a Loop, first give it a title. This title becomes a variable that expects an array of objects in the payload. The Loop repeats your layout according to the number of items in this array.
#### **Example:**
If your layout is:
Your payload should look like this:
```json theme={null}
{
"loop_title": [
{ "name": "John Doe", "picture_src": "https://img.here" },
{ "name": "Sophie Lorem", "picture_src": "https://img.here" }
]
}
```
This results in your layout repeating twice:
### Stacking loops
You can also create nested loops by stacking them. This allows for more complex data structures.
#### **Example of a nested layout:**
Expected payload for nested loops:
```json theme={null}
{
"loop_title": [
{
"name": "John Doe",
"picture_src": "https://img.here",
"characteristics": [
"characteristics1",
"characteristics2",
"characteristics3"
]
},
{
"name": "Sophie Lorem",
"picture_src": "https://img.here",
"characteristics": [
"characteristics1",
"characteristics2",
"characteristics3"
]
}
]
}
```
Resulting on this final result:
# Markdown
Source: https://docs.pdfnoodle.com/knowledge-base/components/markdown
Our Markdown component lets you work with both dynamic markdown strings (passed in the payload) or static markdown.
It follows our styling standards, supporting a wide range of elements, including:
* Headings
* Lists
* Links
* Images
* Tables
* Code blocks
* Blockquotes
* Inline code
* Horizontal rules
* Line breaks
### Example of markdown syntax:
````markdown theme={null}
# Styled Markdown Example
This is a paragraph with **bold** and _italic_ text.
## Lists Example
- First item
- Second item with **bold**
- Nested item with _italic_
- Another nested item
## Code Example
```javascript
function greeting(name) {
return `Hello, ${name}!`;
}
```
## Blockquote Example
> This is a blockquote.
> It can span multiple lines.
## Table Example
| Feature | Description |
| ------- | ------------------------- |
| Tables | Organized data display |
| Lists | Bullet points and numbers |
| Code | Syntax highlighted blocks |
## Link Example
[Visit pdf noodle blog](https://app.pdfnoodle.com/blog)
### Image Example

````
### Send markdown as payload
You can also mark the option "Send markdown as payload" to send it dynamically on the payload of your API call. The variable name will automatically be set using the "Identifier" field.
You can see more on payload schema of API calls on the [API
documentation](https://app.gitbook.com/o/6WKbIlYX72bYYe0wYnOa/s/5XMpOekw27OhJlJjfwYv/).
### Horizontal Margin convention
Apart from the Page component, every other component can be configured with a horizontal margin for a cleaner, more polished layout. The horizontal margin adheres to our spacing convention:
* **No space** - 0 px
* **Small space** - 12px
* **Medium space** - 20px
* **Large space** - 40px
### Visibility convention
If you want to conditionally render a text component, you can set a variable that controls its visibility. When this variable is included in the variables payload, the component will display; if it’s absent, the component won’t appear in the final render.
# Metrics
Source: https://docs.pdfnoodle.com/knowledge-base/components/metrics
The Metrics component is perfect for displaying KPIs or highlights inside your PDF templates. With it, you can quickly show numbers, icons, and subtitles, ideal for reports, dashboards, or performance summaries.
***
### How It Works
Each metric card contains:
* Icon – choose from our icon library and customize the color.
* Name – label that describes the metric.
* Value – dynamically filled using variables.
* Subtitle – optional, can include HTML for styled notes (e.g., growth indicators).
You can add multiple metrics within the same component. By default, they display side by side.
***
### Sending Data Dynamically
Metrics are automatically bound to variables based on the component identifier and metric name.
For example, with identifier "Metrics title" and two metrics (Metrics 01 and Metrics 02), the payload looks like this:
```json theme={null}
{
"metrics_title": {
"metrics_01": {
"value": "your metrics_title.metrics_01.value here",
"subtitle": "your metrics_title.metrics_01.subtitle here"
},
"metrics_02": {
"value": "your metrics_title.metrics_02.value here",
"subtitle": "your metrics_title.metrics_02.subtitle here"
}
}
}
```
#### Subtitle with HTML
You can pass HTML tags on the subtitle to customize formatting, such as:
```json theme={null}
"subtitle": "
+12% vs last month
"
```
This is especially useful for trend indicators or colored annotations.
***
### Design & Layout Options
* **Show title**: Toggle to display or hide the component title.
* **Margin convention**: Control horizontal padding - none (0px) , small (12px), medium (20px) or large (40px)
* **Justify options**: Align metrics left, center, right, or spaced evenly.
* **Visibility convention**: If no data is sent for a metric, it won’t render.
***
### Best Practices
* ✅ Use up to 4 metrics per row – this ensures an optimized layout for readability.
* 🔄 More than 4 metrics – the component will wrap automatically.
* 📏 Want all metrics on one line? Increase the page width via the Style button.
💡**Tip:** Combine icons with styled subtitles for extra clarity, e.g., show
an arrow icon and a green “+12%” subtitle to highlight growth.
# Page Break
Source: https://docs.pdfnoodle.com/knowledge-base/components/page-break
The Page Break component ensures precise control over how your document flows during PDF generation. It automatically forces a page break (page-break-before: always), so you can decide exactly where a new page should begin. This is especially useful for separating sections like chapters, invoices, or grouped content in your document.
***
### How It Works
When included in your template, the Page Break component applies a CSS rule (*page-break-before: always*), ensuring that everything after it starts on a new page.
**Hint:** Use this component anytime you want to control the exact pagination
of your generated PDF, rather than relying only on automatic page flow.
### Conditional Rendering
You can control the visibility of the Page Break using the Conditional rendering option:
* Add a variable inside the Show if field.
* If the variable is present in your payload, the Page Break will appear.
* If the variable is absent, the Page Break won’t render.
#### Example:
```
{
"page_break_trigger": true
}
```
If you add `{{ page_break_trigger }}` to the “Show if” field, the page break will only apply when this variable exists.
# PDF Styling
Source: https://docs.pdfnoodle.com/knowledge-base/components/pdf-styling
Here's where you'll configure your PDF styling, setting up global configurations such as:
Here's where you'll configure your PDF styling, setting up global configurations such as:
* Font family
* PDF width
* PDF Layout (Portrait or Landscape)
* Gap: The space between components
* Vertical Margin: Separate settings for the first page and subsequent pages
* Custom header on all pages
* Custom footer on all pages
The **PDF height** is automatically calculated based on the A4 page proportion
(1 : 1.414).
### Space convention
At pdf noodle, we standardize all spacing properties so you don’t have to think twice. Our spacing convention is as follows:
* **No space** - 0 px
* **Small space** - 12px
* **Medium space** - 20px
* **Large space** - 40px
### Vertical Margin convention
We differentiate between the vertical margin for the first page and for all other pages. Typically, the first page may feature a header, meaning you might prefer no vertical margin there, while additional pages can benefit from some spacing for page breaks.
Also, note that we only manage vertical margins. Horizontal margins should be adjusted directly within the individual components in your template.
***
### Custom Footer
You can choose to insert a custom footer at the bottom of every PDF page generated.
The footer is highly customizable:
* Add plain text or dynamic variables (`{{variable}}`).
* Insert HTML strings and inline styles for richer formatting.
* Use predefined classes to quickly render dynamic info:
* .pageNumber → shows the current page number
* .totalPages → shows the total number of pages
* .date → shows the current date
* .title → shows the template title
Additionally, you can configure the Footer Height. This value reserves vertical space at the bottom of each page to prevent the footer content from overlapping your PDF body. Set it according to the expected height of your footer design.
💡 **Tip:** Passing variables in the payload allows you to display dynamic
data in the footer (e.g., client name, report ID, or any custom field).
The result will be like this:
### Custom Header
Similarly, you can add a custom header at the top of every PDF page.
The header supports the same options as the footer:
* Add static or dynamic text with variables (`{{variable}}`).
* Insert HTML strings and styles for full design flexibility.
* Use the predefined classes for auto-filled elements:
* .pageNumber
* .totalPages
* .date
* .title
The Header Height setting reserves vertical space at the top of the page so your header doesn’t overlap with the document content. Adjust it based on the size of your header elements.
**💡 Tip:** Just like the footer, you can pass variables in the payload to
render dynamic information (like user names, project titles, or branding
details) in your header.
If you didn’t find what you were looking for regarding the PDF style, feel
free to contact us at [support@pdfnoodle.com](mailto:support@pdfnoodle.com).
We’ll get back to you as quickly as possible!
# Table
Source: https://docs.pdfnoodle.com/knowledge-base/components/table
Our table component lets you list dynamic items without writing loops or extra HTML. It's designed to help you create the perfect table for your document.
### Variables for a table
We automatically generate a variable based on your table title. This variable is an array of objects, with each column becoming a key.
For example, if your table looks like this:
The variables will look like this:
```
{
"table_title": [
{
"description": "your table_title.description value here",
"quantity": "your table_title.quantity value here",
"total": "your table_title.total value here"
}
]
}
```
**Each row's variable accepts HTML strings**, so you can include HTML code to
customize cell content. (See our guide on HTML content for more details.)
1. **Making the cell colored and bold**
\`\
This is a red, bold paragraph.\
\`
2. **Inserting an icon along with text**
\` \
\\\
Text next to the icon.\
\
\`
3. **Inserting multiple elements in a cell**
\`\
\
\ Item ID: 123\ \ Item Description: Example Item\\
\
\ Stock Left: 15 \\
\
\`
### Number of columns allowed
There's no hard limit on the number of columns, but we recommend up to 6 columns for an 810px wide page.
If you need more columns, **try reducing the font size or increasing your page
width.**
### Customizing Your Table
You can style your table by adjusting:
* **Horizontal Margin:** none (0px), small (12px), medium (20px) or large (20px)
* **Font size:** extra small (10px), small (12px), medium (14px) or large (16px)
* **Show title:** Choose to display the table title
* **Card border:** Toggle a rounded card border around the table
* **Rounded table:** Round the table borders with an 8px radius
* **Rounded:** none (0px), small (6px), medium (8px) or full (9999px)
* **Headers background color:** Set the first row's background color
* **Headers text color:** Set the first row's text color
* **Rows background color:** Set the background color for other rows
* **Rows text color:** Set the text color for other rows
### Visibility convention
By default, the table won't render if you don't send the table variable in the PDF payload or if it's an empty array. To render the table even when the variable is missing or empty, simply fill in the "empty state" input so the table will display the empty state message.
# Text
Source: https://docs.pdfnoodle.com/knowledge-base/components/text
Our flexible Text component is designed for any text content you want to include in your template. Unlike other components, you'll edit the text directly on the canvas preview for a more intuitive experience.
You can customize the following aspects of your text:
* **Style**: Headings 1, 2, 3, 4, paragraph or small text
* **Bold**
* **Italic**
* **Strikethrough**
* **Hyperlink**: Will only work on PDF output
* **Make it a variable**
* **List**: Ordered or Unordered
* **Align**: Left, center or right
* **Text color**
You can also edit the following properties:
* **Text container width**: If it should take all space or specific width
* **Horizontal margin**
### Font Size and Weight Conventions
We've standardized the font sizes and weights for each text style to ensure a consistent look across your templates. For example:
* **Heading 1:** font-size 24px and font-weight: 500
* **Heading 2:** font-size 20px and font-weight: 500
* **Heading 3:** font-size 18px and font-weight: 500
* **Heading 4:** font-size 16px and font-weight: 500
* **Paragraph:** font-size 14px and font-weight: 400
* **Small text:** font-size 12px and font-weight: 400
### Variables convention
We use [handlebars](https://handlebarsjs.com/) as our templating engine to differentiate between dynamic variables and static text in your templates. Here's how it works:
• Simple Variable: Enclose your text in double curly brackets: `{{ }}`.
• HTML Variable: Enclose your text in triple curly brackets: `{{{ }}}` to include HTML tags.
When you click on "Make it a variable," the selected text will automatically be transformed into a simple variable for your template.
For more details on Handlebars syntax, please check out our guide in the
[**HTML component**](https://docs.pdforge.com/knowledge-base/components/html).
### Horizontal Margin convention
Apart from the Page component, every other component can be configured with a horizontal margin for a cleaner, more polished layout. The horizontal margin adheres to our spacing convention:
* **No space** - 0 px
* **Small space** - 12px
* **Medium space** - 20px
* **Large space** - 40px
### Visibility convention
If you want to conditionally render a text component, you can set a variable that controls its visibility. When this variable is included in the variables payload, the component will display; if it's absent, the component won't appear in the final render.
# Configuring Amazon S3
Source: https://docs.pdfnoodle.com/knowledge-base/configuring-s3/configuring-amazon-s3
Setting up S3 buckets and IAM credentials can be a bit tricky, so we’ve broken the process down step-by-step:
### Overview
1\. Create a new S3 bucket (private or public)
2\. Create an IAM user
3\. Create an IAM group
4\. Add the user to the group
5\. Attach a policy that allows uploads to your bucket
6\. Generate access and secret keys
7\. Add the S3 connection in pdf noodle
8\. Set it as your default storage
***
### Step 1: Create an S3 Bucket
1\. In your AWS console, go to the S3 service and click Create bucket.
2\. Enter a bucket name and choose a region (e.g. us-east-1).
3\. We recommend making the bucket private. pdf noodle will use signed URLs to upload files securely.
4\. Click Create bucket to finish.
***
### Step 2: Create an IAM User
1\. In the AWS console, go to IAM > Users and click Create user.
2\. Name the user something like pdfnoodle-s3-user.
3\. On the next step, choose Add user to group and click Create group.
4\. Name the group something like pdfnoodle-uploaders.
5\. Complete the user creation wizard.
***
### Step 3: Add Upload Permissions
1\. Go to the group you just created.
2\. Under the Permissions tab, click Add permissions > Create inline policy.
3\. Switch to the JSON tab and paste the following policy:
**For pdf noodle to only have access to upload files:**
```json theme={null}
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:PutObject"],
"Resource": ["arn:aws:s3:::your-bucket-name/*"]
}
]
}
```
**For pdf noodle to have access have access to upload and get files:**
```json theme={null}
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject"
],
"Resource": "arn:aws:s3:::your-bucket-name/*"
}
]
}
```
Replace your-bucket-name with your actual bucket name.
**Hint:** If you want this user to have access to *all* your buckets (not recommended), use: arn:aws:s3:::\*/\*.
4. Click Review policy, name it (e.g. pdfnoodle-s3-policy), and save.
***
### Step 4: Generate Access Keys
1\. Go back to the IAM user you created.
2\. Under the Security credentials tab, scroll to Access keys.
3\. Click Create access key, selecting Third-party service as the use case.
4\. Copy the Access Key ID and Secret Access Key — you won’t be able to see the secret again.
***
### Step 5: Connect Your Bucket to pdf noodle
1\. In pdf noodle, go to Settings > S3 Configuration.
2\. Click Add S3 Connection, then enter:
* Provider: AWS
* Access Key and Secret Key
* Bucket Name
* Bucket Region
* *(Optional)*: Enable **Upload access only** if you don’t want pdf noodle to read from the bucket.
**Note:** With **Upload access only** enabled, we won’t return the file in the API response—only its key.
3\. Click Continue. If everything’s configured properly, you’ll see a success message.
pdf noodle will attempt to upload a test file to:
`pdfnoodle_test/delete_me_{date}.txt`
Since we don’t have delete permissions, you’ll need to remove that file manually later.
Hint: If you see an error, double-check your credentials and bucket permissions.
***
### Step 6: Set as Default Storage
Go back to your S3 settings in pdf noodle and select your new connection as the default bucket. **All future PDFs will now be saved there. 🎉**
**Hint:** You can also set on which `s3_bucket` and `s3_key` you want to save PDFs individually, by passing an extra parameter. [You can see more about it here](https://app.gitbook.com/o/6WKbIlYX72bYYe0wYnOa/s/5XMpOekw27OhJlJjfwYv/)!
# Configuring Google Cloud Storage
Source: https://docs.pdfnoodle.com/knowledge-base/configuring-s3/configuring-google-cloud-storage
This guide walks you through creating your bucket, enabling HMAC authentication, assigning the correct IAM permissions, and adding your credentials to pdf noodle.
***
### 1. Create or Select Your GCS Bucket
If you don’t already have a bucket:
1. Go to Google Cloud Console → Cloud Storage → Buckets
2. Click Create
3. Choose:
* **Bucket Name**: Your choice (I'm using `pdfnoodle-gcp-test`)
* **Location**: Region closest to your users
* **Storage class**: Standard (recommended)
* **Public access**: Prevent public access (ON)
* **Access control**: Uniform (required)
4. Finish creation
Here's how my final configuration looks like:
**Important:** Uniform bucket-level access + public access prevention is the
recommended setup for private PDF storage with pdf noodle.
***
### 2. Create a service account
Now let’s set up the service account that pdf noodle will use when interacting with your Google Cloud Storage bucket. This account defines exactly what pdf noodle is allowed to do inside your bucket: uploading files, reading them back, or both.
If you already have a dedicated service account for storage operations, **feel free to move on to the next step.**
**Here's how to do it:**
1. Go to Google Cloud Console → IAM & Admin → Service Accounts
2. Click Create Service Account
3. Enter a name (e.g., pdf-noodle-storage) and click Create and Continue
4. Assign the required permissions:
#### Upload-only (maximum privacy)
Choose this if you want pdf noodle to upload files but never read them.
Add the role:
```
Storage Object Creator
```
#### Upload + Read (full integration)
Choose this if you want pdf noodle to upload, read, or validate files.
Add both roles:
```
Storage Object Creator
Storage Object Viewer
```
**Important:** This guide uses `Storage Object Creator` under the assumption
that you *won’t* be overwriting files with the same key (filename). If your
workflow requires pdf noodle to replace existing files, switch to `Storage
Object Admin` to avoid permission errors during upload.
Here's an example on how your Service Account Permissions should look like:
***
### 3. Create Credentials and Grant Access to Your Bucket
Now that your service account is ready, you’ll generate the **HMAC access key and secret** that pdf noodle will use to communicate with Google Cloud Storage through the S3-compatible API.
1. Go to Google Cloud Console → Cloud Storage → Settings → Interoperability
2. Scroll to the HMAC keys section
3. Click Create a key for a service account
4. Select the service account you just created
5. Click Create Key to generate your:
* **Access Key ID**
* **Secret Access Key**
These credentials allow pdf noodle to authenticate with your bucket using
S3-style requests. Make sure to store the secret key securely. It is only
shown once.
***
## 4. Add GCS Credentials to pdf noodle
1\. In pdf noodle, go to Settings > S3 Configuration.
2\. Click Add S3 Connection, then enter:
* Provider: Google Cloud Storage
* Access Key and Secret Key
* Bucket Name
* *(Optional)*: Enable **Upload access only** if you don’t want pdf noodle to read from the bucket.
**Note:** With **Upload access only** enabled, we won’t return the file in the
API response—only its key.
3\. Click Continue. If everything’s configured properly, you’ll see a success message.
pdf noodle will attempt to upload a test file to:
`pdfnoodle_test/delete_me_{date}.txt`
Since we don’t have delete permissions, you’ll need to remove that file manually later.
Hint: If you see an error, double-check your credentials and bucket
permissions.
***
### Step 6: Set as Default Storage
Go back to your S3 settings in pdf noodle and select your new connection as the default bucket. **All future PDFs will now be saved there. 🎉**
**Hint:** You can also set on which `s3_bucket` and `s3_key` you want to save
PDFs individually, by passing an extra parameter. [You can see more about it
here](https://app.gitbook.com/o/6WKbIlYX72bYYe0wYnOa/s/5XMpOekw27OhJlJjfwYv/)!
# Configuring Supabase Storage
Source: https://docs.pdfnoodle.com/knowledge-base/configuring-s3/configuring-supabase-storage
Setting up Supabase Storage buckets and access keys can be a bit tricky, so we've broken the process down step-by-step:
### Overview
1. Create a new Storage bucket in Supabase
2. Generate access keys (Access Key ID and Secret Access Key)
3. Fetch your endpoint URL
4. Add the Supabase Storage connection in pdf noodle
5. Set it as your default storage
***
### Step 1: Create a Storage Bucket
1. In your Supabase dashboard, navigate to **Storage**.
2. Click **New bucket** to create a new bucket.
3. Enter a bucket name (e.g., `pdfnoodle-storage`).
4. Choose your privacy settings:
* **Public**: Files are accessible via public URLs
* **Private**: Files require authentication (recommended for PDFs)
5. Click **Create bucket** to finish.
**Hint:** We recommend making the bucket private. pdf noodle will use signed
URLs to upload files securely.
***
### Step 2: Generate Access Keys
1. In your Supabase dashboard, go to **Settings** > **API**.
2. Scroll down to the **Storage** section.
3. Under **S3 Access Keys**, click **Generate new key**.
4. Copy the **Access Key ID** and **Secret Access Key** — you won't be able to see the secret again.
**Important:** Store these credentials securely. The secret key will only be
shown once.
***
### Step 3: Fetch Your Endpoint URL
1. In your Supabase dashboard, go to **Settings** > **API**.
2. Scroll to the **Storage** section.
3. Find the **S3 Endpoint URL** (it should look like: `https://[project-ref].supabase.co/storage/v1/s3`).
4. Copy this endpoint URL — you'll need it when configuring pdf noodle.
**Note:** The endpoint URL is specific to your Supabase project and includes
your project reference ID.
***
### Step 4: Connect Your Bucket to pdf noodle
1. In pdf noodle, go to **Settings** > **S3 Configuration**.
2. Click **Add S3 Connection**, then enter:
* **Provider**: Supabase Storage
* **Access Key** and **Secret Key** (from Step 2)
* **Bucket Name** (the bucket you created in Step 1)
* **Endpoint URL** (from Step 3)
* **Region**: Leave as **Auto** (will be automatically detected)
* *(Optional)*: Enable **Upload access only** if you don't want pdf noodle to read from the bucket.
**Note:** With **Upload access only** enabled, we won't return the file in the
API response—only its key.
3. Click **Continue**. If everything's configured properly, you'll see a success message.
pdf noodle will attempt to upload a test file to:
`pdfnoodle_test/delete_me_{date}.txt`
Since we don't have delete permissions, you'll need to remove that file manually later.
**Hint:** If you see an error, double-check your credentials, bucket name, and
endpoint URL.
***
### Step 5: Set as Default Storage
Go back to your S3 settings in pdf noodle and select your new connection as the default bucket. **All future PDFs will now be saved there. 🎉**
**Hint:** You can also set on which `s3_bucket` and `s3_key` you want to save
PDFs individually, by passing an extra parameter. [You can see more about it
here](https://app.gitbook.com/o/6WKbIlYX72bYYe0wYnOa/s/5XMpOekw27OhJlJjfwYv/)!
# Embedding the template builder in your application
Source: https://docs.pdfnoodle.com/knowledge-base/guides/embedding-the-template-builder-in-your-application
**Perfect for SaaS businesses that want to offer personalized PDF templates to their users.**
**Hint: This feature is only available on our Scale plan.**
***
### How to Activate Embedding
To start embedding templates, you first need to activate embedding for each specific template you want to share.
1. Go to the “Embeds” menu on the sidebar.
2. Click “Embed new template”.
3. Select the template you want to enable for embedding.
Once you activate embedding, you’ll see detailed instructions on how to integrate it into your app.
***
### Generating the JWT Token
The embedding process uses a JWT token to securely identify which template and customer are being customized.
You just need to follow the instructions on the embedding page.
You’ll need to generate this token using the following payload structure:
```json theme={null}
{
"companyId": "your pdf noodle workspace UUID",
"templateId": "the ID of the template being embedded",
"externalId": "your customer’s unique ID in your app",
"exp": "timestamp when token expires"
}
```
#### 🔐 Secret Key
You’ll find your workspace secret key on the Embeds page. You’ll need this to sign the JWT token.
**Hint:** You can regenerate your Secret Key anytime you want, but that will make the previous secret key invalid.
***
### Embedding the Builder
After generating the token, create an iframe in your application using this format:
```html theme={null}
```
Your customer will now see a white-label version of the pdf noodle builder, so they can freely customize their version of the template.
Customers can always revert to the default version of the template at any time.
***
### Understanding the Data Dictionary
Each template is powered by a data schema that defines the variables you send in your API payload.
The data dictionary ensures customers only use variables that exist in your schema — maintaining consistency between your API and their edits.
#### Key Points:
* Customers can only use existing variables (they’ll see them listed by their label in the text editor).
* They cannot create new variables not defined in the schema.
* This is why they also can’t add new columns to tables, new metrics to the Metrics component, or new series to Chart components.
**Tip:** Keep your data dictionary updated whenever your API schema changes. It ensures smooth synchronization between your system and the embedded editor.
***
### Generating PDFs with Customer Customizations
Once your customer customizes a template, generating a PDF with their version is easy, just include their externalId in your API payload.
If pdf noodle detects a customization for that customer, it’ll automatically generate their version.
If not, it will fallback to the default template.
#### Example API Call
```sh theme={null}
curl --location 'https://api.pdfnoodle.com/v1/pdf/sync' \
--header 'Authorization: Bearer pdfnoodle_api_123456789' \
--header 'Content-Type: application/json' \
--data '{
"templateId": "check",
"externalId": "customer-uuid",
"data":{...}
}
'
```
**📘 Documentation:** Check out the full [API Reference](https://app.gitbook.com/o/6WKbIlYX72bYYe0wYnOa/s/5XMpOekw27OhJlJjfwYv/) for details on how to send custom data and handle customer versions.
If you still have questions or feedback about the embedding feature, contact us at [support@pdfnoodle.com](mailto:support@pdforge.com) — we’ll be happy to help!
# Saving to your S3 storage
Source: https://docs.pdfnoodle.com/knowledge-base/guides/saving-to-your-s3-storage
These files expire after 30 days—after that, they’re no longer accessible. So even if you haven’t enabled custom storage, we recommend downloading or backing up important files on your end.
If you’d prefer a more permanent and private storage option, you can connect your own Amazon S3 bucket. This gives you full control over access, retention, and security. For example, you can configure pdf noodle with *upload-only* permissions, so we can send files to your bucket without being able to read or download them.
**Heads up**: This feature is only available on the **High** plan.
***
### Available S3 Providers
Currently, we support **Amazon S3**, **Google Cloud Storage**, and **Supabase Storage** (both public and private buckets).
Check out our step-by-step guide to set up your S3 bucket with pdf noodle:
* [Amazon S3 ](/knowledge-base/configuring-s3/configuring-amazon-s3)
* [Google Cloud Storage](/knowledge-base/configuring-s3/configuring-google-cloud-storage)
* [Supabase Storage](/knowledge-base/configuring-s3/configuring-supabase-storage)
If you’d like us to support another provider, just email us at
[support@pdfnoodle.com](mailto:support@pdforge.com)—we’d love to hear from
you!
***
### How to Set a Default Bucket
On your S3 settings page, you can choose a default bucket where all new PDF/PNG renders will be saved.
***
### F.A.Q.
Not yet—all renders currently go to a single bucket. Support for multiple buckets is coming soon!
Only if you allow it. When using *upload-only* mode, PDForge can upload files but cannot read or retrieve them. That means API responses will include the file key—not the file itself.
You can’t edit an existing connection, but you can archive it and create a new one at any time. A connection is only saved after a test file is successfully uploaded, so you’re safe to experiment.
Yes—all access and secret keys are AES-256 encrypted in our database.
# Knowledge Base
Source: https://docs.pdfnoodle.com/knowledge-base/index
Welcome to the PDF Noodle Knowledge Base. Learn how to use the platform, build templates, and configure your account.
## Platform
Get started with PDF Noodle platform
Set up your PDF Noodle account
Learn how to use the template builder
Manage your team members
## Components
Explore all available components for building PDF templates
## Guides
Configure S3 storage for your PDFs
Embed the template builder in your app
# Creating your account
Source: https://docs.pdfnoodle.com/knowledge-base/platform/creating-your-account
**We’ll only begin your free trial once you’ve created your first template. 😉**
### How's the onboarding process?
Our onboarding process is designed to be straightforward and consists of a few simple steps:
#### Step 1: Email Verification
Start by entering and verifying your email address. We know managing multiple passwords can be a hassle, so you have the option to use One Time Password (OTP) or Google OAuth.
You can see more on [**User Authentication**](https://docs.pdforge.com/knowledge-base/platform/user-authentication) here.
#### Step 2: Company Workspace
Create your company workspace so we can tailor the product to your specific needs.
#### Step 3: First Template Creation
Experience our [**Template Builder** ](https://docs.pdforge.com/knowledge-base/platform/template-builder)firsthand by creating your first template. We believe it wouldn’t be fair to charge you before you’ve had the chance to explore our tool fully.
#### Step 4: 7-Day Free Trial
Choose your plan and provide your credit card information. You won’t be charged until the trial period ends, and you can cancel at any time by clicking on [**Manage Subscription**](https://docs.pdforge.com/knowledge-base/platform/subscription-management).
If you need to pause the process, don’t worry—**you will resume from where you left off without starting over.**
# PDF Tools Usage & Plan Limits
Source: https://docs.pdfnoodle.com/knowledge-base/platform/pdf-tools-usage
Understand how PDF tool operations are tracked and how they count toward your plan's usage quota.
## Overview
PDF tools — including [Merge PDFs](/api-reference/tools/merge-pdfs), [Split PDF](/api-reference/tools/split-pdf), [Compress PDF](/api-reference/tools/compress-pdf), [Convert Markdown to PDF](/api-reference/tools/convert-markdown-to-pdf), and [Update PDF Metadata](/api-reference/tools/update-pdf-metadata) — are now fully tracked operations.
Every tool operation:
* Creates a record visible in your [dashboard logs](https://app.pdfnoodle.com/logs)
* Stores the output file persistently (no more expiring temporary URLs)
* Can be re-downloaded at any time from the logs table
## Plan-Based Usage
How tool operations count toward your quota depends on your plan:
| Plan | PDF Generation | Tool Operations | Quota Impact |
| ------------ | -------------------- | -------------------- | -------------------------------------------- |
| **Starter** | Counts toward volume | Counts toward volume | All operations share the same quota |
| **Business** | Counts toward volume | **Unlimited** | Only PDF generation counts toward your quota |
| **Scale** | Counts toward volume | **Unlimited** | Only PDF generation counts toward your quota |
### Starter Plan
On the Starter plan, all operations — both PDF generation and tool operations — count toward your `contractedVolume`. For example, if your plan includes 1,000 PDFs per month and you generate 800 PDFs and merge 200 files, your usage is 1,000/1,000.
### Business & Scale Plans
On Business and Scale plans, tool operations are **unlimited** and do **not** count toward your PDF generation quota. Only `pdf_generation` operations count. This means you can merge, split, compress, convert, and update metadata on as many files as you need without impacting your quota.
In the dashboard logs table, tool operations on Business and Scale plans display an info icon indicating they don't count toward your usage quota.
## Persistent File Storage
All tool output files are now stored in our persistent storage — the same infrastructure used for PDF generation. This means:
* **No more expiring URLs**: While the URL in the API response still has an expiration (set via the `expiration` parameter), the file itself is stored permanently.
* **Re-download anytime**: Click on any filename in the logs table to generate a fresh download URL.
* **Audit trail**: Every operation is recorded with status, execution time, file size, and the API key used.
## Operation Types
Each tool creates records with a specific operation type:
| Tool | Operation Type | Log Label |
| ----------------------- | ------------------------- | --------------- |
| Merge PDFs | `merge_pdf` | Merge PDF |
| Split PDF | `split_pdf` | Split PDF |
| Compress PDF | `compress_pdf` | Compress PDF |
| Convert Markdown to PDF | `convert_markdown_to_pdf` | Markdown to PDF |
| Update PDF Metadata | `update_pdf_metadata` | Update Metadata |
| PDF Generation | `pdf_generation` | PDF Generation |
## Failed Operations
If a tool operation fails, a record is still created with `ERROR` status. This provides a complete audit trail. Failed operations **do not** count toward your usage quota on any plan.
## Get Signed Upload URL
The [Get Signed Upload URL](/api-reference/tools/get-signed-upload-url) endpoint is a utility for uploading input files to temporary storage. It does **not** create any log records and does **not** count toward usage on any plan.
# Overview
Source: https://docs.pdfnoodle.com/knowledge-base/platform/quickstart
### Dashboard
This is your home page, where you can:
• Monitor your product usage.
• View your API key.
• Check the last 5 PDFs/images you generated.
***
### Templates
This section lists all your available templates.
Every user on your team has permission to **view**, **edit**, **duplicate**,
or **delete** any template at any time.
You can see all the variables for a template or how to implement it by clicking on it.
***
### Logs
In the Logs section, you’ll find a record of all the renders you made using pdf noodle.
To view a generated PDF, simply click on its filename.
**The log files are retained for 90 days**. After that period, you will no longer be able to access them within pdf noodle, so we recommend saving them directly to your own bucket.
***
### Teammates
Here, you can manage your team members.
Every user on your team has permission to **invite** or **delete** other users
at any time.
***
### F.A.Q.
The usage period starts when the first payment for pdforge was made, after the 7-day trial period ends.
You can access the generated pdf file for up to 30 days. After that, we only maintain the reference of the file, but it's no longer accessible.
Not currently. Every user have the same permission and can do anything inside the application.
If you didn’t find what you were looking for in the knowledge base, feel free to contact us at [support@pdfnoodle.com](mailto:support@pdfnoodle.com). We’ll get back to you as quickly as possible!
You'll have access to your API Key once you start your 7-day free trial, after the onboarding flow.
# Subscription management
Source: https://docs.pdfnoodle.com/knowledge-base/platform/subscription-management
When you click it, you’ll be redirected to Stripe, where you’ll be able to manage:
* See current subscription
* Cancel your subscription
* Update payment methods
* Update billing information
* See invoice history
If you **forgot to cancel** your subscription before your trial ended—or if
you were charged for the following month—you can request a refund by
contacting us at **[support@pdfnoodle.com](mailto:support@pdfnoodle.com)**.
# Teammates management
Source: https://docs.pdfnoodle.com/knowledge-base/platform/teammates-management
We believe that **managing PDF or image generation** within your company shouldn’t be the sole responsibility of the development team—it should be a **collaborative effort** that everyone can participate in, that why **you don't have a limit of users you can invite to pdf noodle**.
### How does teammate management work?
You can manage your teammates using the Settings sidebar menu.
Every user on your team has permission to **invite** or **delete** other users
at any time.
When you invite a user, they will receive an email from pdf noodle with instructions to complete their first login.
For additional details on our authentication methods, check out the [**User
Authentication
page**](https://docs.pdforge.com/knowledge-base/platform/user-authentication).
### Frequently Asked Questions (F.A.Q.)
No. Deleting a user does not affect any templates or other information in pdforge.
Currently, pdforge does not automatically add users with the same domain to your workspace. The new user will create an account, but they won’t be automatically linked to your workspace.
Once a user is linked to a workspace, it cannot be changed directly through the interface. If you need to modify a user’s workspace, please email us at [support@pdforge.com](mailto:support@pdforge.com) so we can help you out.
# Template builder
Source: https://docs.pdfnoodle.com/knowledge-base/platform/template-builder
### What is the template builder?
The Template Builder is your go-to tool for creating reusable templates to generate PDFs and images at scale. With our user-friendly interface, you can build your template in just minutes using a variety of components and AI Agents.
Need help along the way? Check out our [**detailed guide on how to use each component**](https://docs.pdforge.com/knowledge-base/components).
### How to start creating my template?
Whenever you start a new template, you have two options:
1\. Build from Scratch: Manually add and modify each component to tailor your template exactly the way you want.
2\. Let Our AI Agent Help: Have our AI Agent do the heavy lifting and create your first template for you. 💡
Don’t worry—**if you choose the AI option,** you’ll still be able to edit and fine-tune the template afterward.
### How does the template builder work?
The template builder is divided into 5 sections:
* Navbar
* Template details
* Component tree
* Preview Canvas
* Properties layer
#### Navbar
In the Navbar, you can generate PDF samples, save your template, or cancel your edits. To generate a PDF sample, simply fill out the template’s variables, and you’re all set!
**We recommend using our AI Agent to fill all the variables for you.** 😉
**Generating PDF samples won't impact your plan's usage**, so you can use it how many times you'd like without having to worry about it.
#### Template details
On the Template Details screen, you’ll need to provide a template name and a unique template ID. The template ID is used to identify this template during API calls and within our no-code integrations.
#### Component tree
On the component tree, you'll be able to add new components or re-order, duplicate ou delete In the Component Tree, you can add new components, re-order them, rename, duplicate, or delete components as needed.
O**nce you delete a component, you can undo this action**, so be careful on this action.
When reordering components, please note that only the Container component accepts child components. All other components can only be moved vertically.
**Hint:** If you’re trying to insert a component inside a container, try moving it slightly to the right. This will indicate that it’s indented within the container, rather than appearing below it.
#### Preview canvas
On the On the Preview Canvas, you’ll see a real-time preview of how your final PDF will look.
You can also edit some information directly on the Canvas, if you're using the Text Component or double-clicking on the HTML component.
#### Properties layer
When you click on a component, you’ll gain access to its custom properties in the Properties Layer. Modify any of these properties and see the changes reflected in real time on the Preview Canvas.
To better understand what each property do, you should take a look at the [Components Section.](https://docs.pdforge.com/knowledge-base/components)
# User authentication
Source: https://docs.pdfnoodle.com/knowledge-base/platform/user-authentication
### How does One Time Password (OTP) work?
**Step 01: Enter Your Email:**
Type in your email address, and we’ll send a message to your inbox with a temporary code.
**Step 02: Use the Code:**
You can either copy the code and paste it into the “Temporary Password” field or click on “Sign in” to trigger the process automatically.
### What if I didn't receive the email with the temporary password?
**• Check Your Spam Folder:**
If more than a minute has passed and you haven’t received the email, please check your spam folder. Look for an email with the subject “**your pdf noodle temporary token**.”
**• Still No Email?**
If you still can’t find the email, feel free to contact us at [support@pdfnoodle.com](mailto:support@pdforge.com), and we’ll help you out!
# SDKs
Source: https://docs.pdfnoodle.com/sdks/index
Official client libraries for the pdf noodle API
Use our official SDKs to integrate pdf noodle into your application. Each SDK wraps the REST API with typed interfaces, automatic error handling, and built-in polling for long-running PDF generations.
## Available SDKs
TypeScript-first SDK with zero runtime dependencies. Supports ESM and CommonJS.
Python, Go, and other SDKs are on the roadmap.
## Why Use an SDK?
While you can call the pdf noodle API directly with `fetch` or any HTTP client, the SDKs provide:
* **Type safety** — Full TypeScript definitions for every request and response
* **Automatic polling** — When synchronous PDF generation exceeds 30 seconds, the SDK polls the status endpoint automatically with exponential backoff
* **Consistent error handling** — Every method returns `{ data, error }` so you never forget to handle failures
* **Zero configuration** — Just pass your API key and start generating PDFs
# Node.js
Source: https://docs.pdfnoodle.com/sdks/nodejs
Official Node.js SDK for the pdf noodle API
The `pdfnoodle` package is a TypeScript-first Node.js SDK with zero runtime dependencies. It uses native `fetch` (Node.js 20+) and ships as both ESM and CommonJS.
## Installation
```bash npm theme={null}
npm install pdfnoodle
```
```bash pnpm theme={null}
pnpm add pdfnoodle
```
```bash yarn theme={null}
yarn add pdfnoodle
```
## Setup
```typescript theme={null}
import { PdfNoodle } from 'pdfnoodle';
const pdfnoodle = new PdfNoodle('pdfnoodle_api_...');
```
You can also set the `PDFNOODLE_API_KEY` environment variable and omit the constructor argument:
```typescript theme={null}
const pdfnoodle = new PdfNoodle(); // reads from PDFNOODLE_API_KEY
```
## Response Pattern
Every method returns a `{ data, error }` object. When the request succeeds, `data` contains the result and `error` is `null`. When it fails, `data` is `null` and `error` contains the error details.
```typescript theme={null}
const { data, error } = await pdfnoodle.pdf.fromHTML({ html: '
Hello
' });
if (error) {
// error.name is a typed error code like "validation_error" or "rate_limit_exceeded"
console.error(error.name, error.message);
return;
}
// TypeScript knows data is not null here
console.log(data.signedUrl);
```
This pattern makes it impossible to forget error handling — you always destructure both values.
***
## PDFs
The Pdf service generates PDFs from raw HTML or from reusable templates. Both sync methods include **automatic polling** — if the API takes longer than 30 seconds to render, the SDK polls the status endpoint with exponential backoff until the PDF is ready. No extra code needed.
**Methods available:**
| Method | Description |
| ----------------------------------------- | ------------------------------------------------------------------------------------- |
| `pdf.fromHTML(payload, pollingOpts?)` | Convert HTML to PDF. Auto-polls on 202. |
| `pdf.fromHTMLAsync(payload)` | Convert HTML to PDF via webhook (no waiting). Requires `webhook` field. |
| `pdf.fromTemplate(payload, pollingOpts?)` | Generate PDF from a reusable template. Auto-polls on 202. |
| `pdf.fromTemplateAsync(payload)` | Generate PDF from template via webhook. Requires `webhook` field. |
| `pdf.getStatus(requestId)` | Check the status of an async or queued generation (`ONGOING` / `SUCCESS` / `FAILED`). |
### Generate PDF from HTML
Use `pdf.fromHTML()` to convert raw HTML into a PDF. The API renders the HTML with a headless browser and returns a temporary signed URL to download the file.
```typescript theme={null}
import { PdfNoodle } from 'pdfnoodle';
const pdfnoodle = new PdfNoodle('pdfnoodle_api_...');
const { data, error } = await pdfnoodle.pdf.fromHTML({
html: `
Invoice #INV-2025-042
Bill to: Acme Corp
Item
Amount
Web Development
$2,500
Hosting (Annual)
$500
Total: $3,000
`,
pdfParams: {
format: 'A4',
printBackground: true,
margin: { top: '20mm', bottom: '20mm', left: '15mm', right: '15mm' },
},
});
if (error) {
console.error('PDF generation failed:', error.message);
return;
}
console.log('Download your PDF:', data.signedUrl);
// => https://pdforge-production.s3.us-east-2.amazonaws.com/...
console.log('File size:', data.metadata.fileSize);
// => "4.066 kB"
```
**What happens under the hood:**
1. The SDK sends a `POST` to `/v1/html-to-pdf/sync` with your HTML and PDF parameters
2. If the API responds with `200`, the signed URL is returned immediately
3. If it responds with `202` (rendering took >30s), the SDK automatically polls `/v1/pdf/status/:requestId` with exponential backoff until the PDF is ready
If you prefer to handle the result asynchronously via a webhook instead of waiting, use `fromHTMLAsync()` with a `webhook` field — you'll get a `requestId` back immediately and the result will be POSTed to your URL when ready.
### Generate PDF from Template
Use `pdf.fromTemplate()` when you have a reusable template created in the [Template Builder](/knowledge-base/platform/template-builder). You pass the template ID and the dynamic data — the API merges them and returns the PDF.
This is ideal for documents you generate repeatedly with different data (invoices, receipts, certificates, reports).
```typescript theme={null}
import { PdfNoodle } from 'pdfnoodle';
const pdfnoodle = new PdfNoodle('pdfnoodle_api_...');
const { data, error } = await pdfnoodle.pdf.fromTemplate({
templateId: 'your-invoice-template-id',
data: {
invoice_number: 'INV-2025-042',
company_info: {
name: 'Acme Corp',
address_line_1: '123 Main Street, New York, NY',
},
items: [
{ description: 'Web Development', quantity: '1', price: '$2,500' },
{ description: 'Hosting (Annual)', quantity: '1', price: '$500' },
],
total: '$3,000',
due_date: 'March 1, 2025',
},
});
if (error) {
console.error('Failed:', error.message);
return;
}
console.log('Download your PDF:', data.signedUrl);
```
**What happens under the hood:**
1. The SDK sends a `POST` to `/v1/pdf/sync` with your template ID and data
2. The API renders the template using [Handlebars](https://handlebarsjs.com/) syntax, replacing `{{invoice_number}}`, `{{#each items}}`, etc. with your values
3. Same auto-polling behavior as `fromHTML()` if rendering exceeds 30 seconds
Not sure which variables your template expects? Use `pdfnoodle.templates.getVariables(templateId)` to get the schema — see the Templates section below.
***
## Templates
The Templates service lets you manage your reusable PDF templates programmatically. You can create templates using AI, list existing ones, fetch their details, and inspect their variable schemas.
**Methods available:**
| Method | Description |
| ----------------------------------------------------- | ------------------------------------------------------------------------------ |
| `templates.create({ prompt, displayName, fileUrl? })` | Create a template using AI. Optionally pass a reference document URL. |
| `templates.get(templateId)` | Get a template's details, or check creation status for AI-generated templates. |
| `templates.list()` | List all templates in your account. |
| `templates.getVariables(templateId)` | Get the variable schema a template expects (field names and types). |
### Create a template with AI
The `create()` method uses AI to generate a template from a natural language description. Template creation is asynchronous — the method returns a `templateId` immediately, and the template is ready after about 2 minutes.
```typescript theme={null}
import { PdfNoodle } from 'pdfnoodle';
const pdfnoodle = new PdfNoodle('pdfnoodle_api_...');
// Step 1: Start template creation
const { data: created, error: createError } = await pdfnoodle.templates.create({
prompt: 'Create a professional invoice template with company logo, billing address, itemized table with description/quantity/price columns, subtotal, tax, and total. Use a clean blue and white color scheme.',
displayName: 'Professional Invoice',
});
if (createError) {
console.error('Failed to create template:', createError.message);
return;
}
console.log('Template creation started:', created.templateId);
// => "a1b2c3d4e5"
// Step 2: Poll until ready (template creation takes ~2 minutes)
let template;
while (true) {
const { data } = await pdfnoodle.templates.get(created.templateId);
// Check if it's still being created (has a status field)
const status = data as Record;
if (status?.status === 'SUCCESS') {
console.log('Template is ready!');
break;
}
if (status?.status === 'FAILED') {
console.error('Template creation failed');
return;
}
// Still creating, wait 15 seconds
await new Promise((resolve) => setTimeout(resolve, 15000));
}
// Step 3: Check what variables the template expects
const { data: variables } = await pdfnoodle.templates.getVariables(created.templateId);
console.log('Template variables:', JSON.stringify(variables, null, 2));
// => { "invoice_number": "string", "items": [{ "description": "string", ... }], ... }
// Step 4: List all your templates
const { data: list } = await pdfnoodle.templates.list();
console.log('All templates:', list.templates);
// => [{ id: "a1b2c3d4e5", displayName: "Professional Invoice" }, ...]
```
***
## Tools
The Tools service provides PDF utility operations — merging, splitting, compressing, converting Markdown, and updating metadata. Each tool accepts a publicly accessible PDF URL and returns a new signed URL with the result.
**Methods available:**
| Method | Description |
| ------------------------------------------------------------ | ------------------------------------------------------------------------------- |
| `tools.getSignedUploadUrl(fileName?)` | Get a presigned URL to upload a PDF to pdf noodle's storage. |
| `tools.mergePdfs({ urls, finalFilename?, expiration? })` | Merge 2+ PDFs into one. |
| `tools.splitPdf({ url, splitMode?, ranges?, interval? })` | Split a PDF by page ranges or intervals. |
| `tools.compressPdf({ url, compressLevel? })` | Compress a PDF. Levels: `low` (print-ready), `medium` (balanced), `high` (web). |
| `tools.markdownToPdf({ markdown, customCss?, pdfOptions? })` | Convert Markdown to a styled PDF. |
| `tools.updatePdfMetadata({ url, metadata })` | Update a PDF's title, author, keywords, and other metadata fields. |
### Merge multiple PDFs
Combine multiple PDF files into a single document. The PDFs are merged in the order you provide them.
```typescript theme={null}
import { PdfNoodle } from 'pdfnoodle';
const pdfnoodle = new PdfNoodle('pdfnoodle_api_...');
const { data, error } = await pdfnoodle.tools.mergePdfs({
urls: [
'https://example.com/cover-page.pdf',
'https://example.com/chapter-1.pdf',
'https://example.com/chapter-2.pdf',
'https://example.com/appendix.pdf',
],
finalFilename: 'complete-report.pdf',
expiration: 7200, // signed URL valid for 2 hours
});
if (error) {
console.error('Merge failed:', error.message);
return;
}
console.log('Merged PDF:', data.url);
// => https://s3.amazonaws.com/.../complete-report.pdf
console.log('Valid until:', data.urlValidUntil);
```
***
## Polling Options
The `fromHTML()` and `fromTemplate()` methods accept an optional second argument to configure polling behavior when the API returns a `202`:
```typescript theme={null}
const { data, error } = await pdfnoodle.pdf.fromHTML(
{ html: '
Large document
' },
{
pollInterval: 2000, // initial delay between polls (default: 2000ms)
maxAttempts: 20, // max polls before timeout (default: 20)
backoffMultiplier: 1.5, // exponential backoff factor (default: 1.5)
maxPollInterval: 10000, // maximum delay cap (default: 10000ms)
signal: controller.signal, // AbortSignal to cancel polling
},
);
```
### Cancelling a long-running generation
```typescript theme={null}
const controller = new AbortController();
// Cancel after 60 seconds
setTimeout(() => controller.abort(), 60000);
const { data, error } = await pdfnoodle.pdf.fromHTML(
{ html: '
Hello
' },
{ signal: controller.signal },
);
if (error?.name === 'polling_aborted') {
console.log('Generation was cancelled');
}
```
***
## Error Codes
| Error Code | Description |
| ----------------------- | ------------------------------------- |
| `validation_error` | Invalid request parameters (400) |
| `missing_api_key` | No API key provided |
| `invalid_api_key` | API key is not valid (401) |
| `not_found` | Resource not found (404) |
| `rate_limit_exceeded` | Too many requests (429) |
| `internal_server_error` | Server error (500) |
| `pdf_generation_failed` | PDF rendering failed |
| `polling_timeout` | Polling exceeded max attempts |
| `polling_aborted` | Polling was cancelled via AbortSignal |
***
## Resources
* [npm Package](https://www.npmjs.com/package/pdfnoodle) — Install and version history
* [GitHub Repository](https://github.com/pdfnoodle/pdfnoodle-node) — Source code and issues
* [API Reference](/api-reference) — Full REST API documentation