Uploading to Your S3 Bucket
Use customS3PresignedUrl when you want PDFBolt to upload the output PDF directly to your own S3-compatible storage. This keeps the final PDF in your storage environment, where you control retention, access policies, and downstream processing.
How It Works
- Generate an HTTPS pre-signed PUT URL for the final PDF object in your S3-compatible bucket.
- Send the URL as
customS3PresignedUrlin a/v1/sync,/v1/async, or PDF Tools Sync (/v1/tools/*/sync) request. - PDFBolt generates or processes the PDF and uploads it to the pre-signed URL with an HTTP
PUT. - Your application reads and manages the PDF from your own bucket.
Pre-signed URL Requirements
The URL you pass to PDFBolt must:
- Use HTTPS. HTTP URLs are rejected.
- Be no longer than 2048 characters.
- Allow
PUTfor the exact bucket and object key. - Remain valid long enough for the full PDF conversion and upload.
- Accept
Content-Type: application/pdf. - Accept the
Content-Dispositionheader PDFBolt sends during upload.
By default, the Conversion API uploads with:
Content-Type: application/pdf
Content-Disposition: inline
PDF Tools also include the output filename, for example Content-Disposition: inline; filename="compress.pdf". The default filenames are merge.pdf, compress.pdf, and convert-pdfx.pdf.
If either upload header is included in the signature, its value must exactly match the value sent by PDFBolt. When your request includes contentDisposition or filename, generate the pre-signed URL with the matching Content-Disposition value.
Supported S3-Compatible Storage
PDFBolt can upload to S3-compatible storage providers that support HTTPS pre-signed PUT URLs, including:
- Amazon S3
- Cloudflare R2
- DigitalOcean Spaces
- MinIO
- Wasabi
- Backblaze B2
Other S3-compatible providers may work if they support HTTPS pre-signed PUT URLs and the required upload headers.
Example: Generating a Pre-signed URL in Node.js
Install the AWS SDK v3 packages:
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');
const { getSignedUrl } = require('@aws-sdk/s3-request-presigner');
const s3 = new S3Client({
region: process.env.S3_REGION || 'us-east-1',
...(process.env.S3_ENDPOINT ? { endpoint: process.env.S3_ENDPOINT } : {}),
credentials: {
accessKeyId: process.env.S3_ACCESS_KEY_ID,
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY
},
// Some S3-compatible providers, such as MinIO, require path-style URLs.
forcePathStyle: process.env.S3_FORCE_PATH_STYLE === 'true'
});
async function generatePresignedUrl(bucketName, objectKey) {
const command = new PutObjectCommand({
Bucket: bucketName,
Key: objectKey,
ContentType: 'application/pdf',
ContentDisposition: process.env.S3_CONTENT_DISPOSITION || 'inline'
});
const url = await getSignedUrl(s3, command, { expiresIn: 3600 });
console.log(url);
return url;
}
generatePresignedUrl('your-bucket-name', `pdfbolt/document-${Date.now()}.pdf`)
.catch((error) => {
console.error('Error generating pre-signed URL:', error);
process.exit(1);
});
S3_REGION=us-east-1 \
S3_ACCESS_KEY_ID=your-access-key-id \
S3_SECRET_ACCESS_KEY=your-secret-access-key \
node generatePresignedUrl.js
For S3-compatible providers other than AWS S3, also set S3_ENDPOINT. For providers that require path-style URLs, set S3_FORCE_PATH_STYLE=true.
The output should look like this:
https://your-bucket.s3.amazonaws.com/pdfbolt/document-1714580000000.pdf?<presigned-query-params>
- Generate a separate pre-signed URL for each PDF. Reusing the same bucket/key can overwrite an existing object.
- Generate the URL on your server, not in browser code.
- Keep storage credentials in environment variables or a secret manager.
- Store the bucket and object key in your application so you can find the PDF later without relying on the pre-signed URL.
- Set
expiresInlong enough for the conversion to finish, especially for large pages or slow external assets.
Use the Pre-signed URL with PDFBolt
Sync Conversion
Use the generated URL as customS3PresignedUrl in the /v1/sync request body:
{
"url": "https://example.com",
"customS3PresignedUrl": "https://your-bucket.s3.amazonaws.com/pdfbolt/document.pdf?<presigned-query-params>"
}
When the upload succeeds, the response has documentUrl: null, expiresAt: null, and isCustomS3Bucket: true because the PDF is already in your bucket.
{
"requestId": "09dd133a-a064-44b6-80f5-b2a7571f77ed",
"status": "SUCCESS",
"errorCode": null,
"errorMessage": null,
"documentUrl": null,
"expiresAt": null,
"isAsync": false,
"duration": 664,
"documentSizeMb": 0.02,
"isCustomS3Bucket": true
}
Async Conversion
For /v1/async, include both webhook and customS3PresignedUrl:
{
"url": "https://example.com",
"webhook": "https://your-app.com/webhooks/pdfbolt",
"customS3PresignedUrl": "https://your-bucket.s3.amazonaws.com/pdfbolt/document.pdf?<presigned-query-params>"
}
The initial API response returns a requestId. When the conversion succeeds, PDFBolt sends a webhook with documentUrl: null, expiresAt: null, and isCustomS3Bucket: true because the PDF is already in your bucket.
{
"requestId": "fadb0a9c-a5f5-4d15-8588-b15bde7c201d",
"status": "SUCCESS",
"errorCode": null,
"errorMessage": null,
"documentUrl": null,
"expiresAt": null,
"isAsync": true,
"duration": 601,
"documentSizeMb": 0.02,
"isCustomS3Bucket": true
}
See Async Workflow Details for the full async flow.
PDF Tools Sync Endpoints
Include customS3PresignedUrl in a PDF Tools Sync request. For example, send this body to /v1/tools/compress/sync:
{
"url": "https://pdfbolt.com/samples/pdf-compression-sample.pdf",
"customS3PresignedUrl": "https://your-bucket.s3.amazonaws.com/pdfbolt/compressed.pdf?<presigned-query-params>"
}
If you use the Node.js script above, set S3_CONTENT_DISPOSITION to inline; filename="compress.pdf" for this example. This matches the default header sent by Compress.
Merge (/v1/tools/merge/sync) and PDF/X (/v1/tools/convert-pdfx/sync) also support this option. On success, documentUrl and expiresAt are null, and isCustomS3Bucket is true.
Troubleshooting
Upload Error
When PDFBolt receives a non-2xx response while uploading the PDF to your pre-signed URL, the operation fails with errorCode: "CUSTOM_S3_UPLOAD_ERROR". For /v1/sync and PDF Tools Sync endpoints (/v1/tools/*/sync), PDFBolt returns the error in an HTTP 400 response. For /v1/async, PDFBolt delivers it in the final webhook with status: "FAILURE". In both cases, the errorMessage includes the storage provider's response code and body.
This usually means one of the following:
- The pre-signed URL expired before the PDF upload started.
- The URL doesn't allow
PUTfor the exact bucket/key. - The signing identity doesn't have
s3:PutObjector equivalent write permission. - The bucket or object key is wrong.
- The provider rejected an upload header, usually
Content-TypeorContent-Disposition. - The bucket policy or provider allowlist rejects PDFBolt's upload request.
If your storage provider uses IP allowlisting, see PDFBolt IP Addresses.
Pre-signed URL Rejected Before Conversion
Make sure customS3PresignedUrl uses HTTPS and is no longer than 2048 characters. HTTP URLs and longer URLs are rejected before PDF generation starts.
Next Steps
📄️ Sync Endpoint
/v1/sync endpoint reference
📄️ Async Endpoint
/v1/async endpoint reference
📄️ Async Workflow Details
Conceptual overview of the async flow
📄️ Error Handling
HTTP status codes and recommended actions