Skip to main content

AWS S3

Prerequisites

  • A PolyDoc account and API key — sign up for free, no credit card required. The free plan includes 150 PDF conversions per month.
  • An AWS account with an S3 bucket. Create a bucket
  • IAM credentials with s3:PutObject permission on the target bucket.

Integration

Generate a presigned URL

Use the AWS SDK to generate a short-lived presigned PUT URL. The URL grants write access to a single object — no bucket-level credentials are shared with PolyDoc.
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";

// npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner

const s3 = new S3Client({ region: "eu-central-1" });

const presignedUrl = await getSignedUrl(
s3,
new PutObjectCommand({
Bucket: "my-bucket",
Key: "invoices/inv-001.pdf",
ContentType: "application/pdf",
}),
{ expiresIn: 300 } // 5 minutes
);
import boto3

# pip install boto3

s3 = boto3.client("s3", region_name="eu-central-1")

presigned_url = s3.generate_presigned_url(
"put_object",
Params={
"Bucket": "my-bucket",
"Key": "invoices/inv-001.pdf",
"ContentType": "application/pdf",
},
ExpiresIn=300, # 5 minutes
)
require "aws-sdk-s3"

# gem install aws-sdk-s3

s3 = Aws::S3::Resource.new(region: "eu-central-1")
presigner = Aws::S3::Presigner.new(client: s3.client)

presigned_url = presigner.presigned_url(
:put_object,
bucket: "my-bucket",
key: "invoices/inv-001.pdf",
expires_in: 300, # 5 minutes
content_type: "application/pdf"
)
package main

import (
"context"
"fmt"
"time"

"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/s3"
)

// go get github.com/aws/aws-sdk-go-v2/config
// go get github.com/aws/aws-sdk-go-v2/service/s3

func main() {
cfg, _ := config.LoadDefaultConfig(context.TODO())
client := s3.NewFromConfig(cfg)
presignClient := s3.NewPresignClient(client)

req, _ := presignClient.PresignPutObject(context.TODO(), &s3.PutObjectInput{
Bucket: strPtr("my-bucket"),
Key: strPtr("invoices/inv-001.pdf"),
ContentType: strPtr("application/pdf"),
}, s3.WithPresignExpires(5*time.Minute))

fmt.Println(req.URL)
}

func strPtr(s string) *string { return &s }
<?php
require 'vendor/autoload.php';

// composer require aws/aws-sdk-php

use Aws\S3\S3Client;

$s3 = new S3Client([
'version' => 'latest',
'region' => 'eu-central-1',
]);

$cmd = $s3->getCommand('PutObject', [
'Bucket' => 'my-bucket',
'Key' => 'invoices/inv-001.pdf',
'ContentType' => 'application/pdf',
]);

$request = $s3->createPresignedRequest($cmd, '+5 minutes');
$presignedUrl = (string) $request->getUri();
use aws_config::BehaviorVersion;
use aws_sdk_s3::{
presigning::PresigningConfig,
Client,
};
use std::time::Duration;

// aws-sdk-s3 = "1" in Cargo.toml

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = aws_config::defaults(BehaviorVersion::latest())
.region("eu-central-1")
.load()
.await;
let client = Client::new(&config);

let presigning_config = PresigningConfig::expires_in(Duration::from_secs(300))?;

let presigned = client
.put_object()
.bucket("my-bucket")
.key("invoices/inv-001.pdf")
.content_type("application/pdf")
.presigned(presigning_config)
.await?;

println!("{}", presigned.uri());
Ok(())
}
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest;
import java.time.Duration;

// implementation 'software.amazon.awssdk:s3:2.x.x' in build.gradle

public class Main {
public static void main(String[] args) {
try (S3Presigner presigner = S3Presigner.builder()
.region(Region.EU_CENTRAL_1)
.build()) {

PutObjectRequest objectRequest = PutObjectRequest.builder()
.bucket("my-bucket")
.key("invoices/inv-001.pdf")
.contentType("application/pdf")
.build();

PutObjectPresignRequest presignRequest = PutObjectPresignRequest.builder()
.signatureDuration(Duration.ofMinutes(5))
.putObjectRequest(objectRequest)
.build();

String presignedUrl = presigner.presignPutObject(presignRequest).url().toString();
System.out.println(presignedUrl);
}
}
}
using Amazon;
using Amazon.S3;
using Amazon.S3.Model;

// NuGet: AWSSDK.S3

var s3 = new AmazonS3Client(RegionEndpoint.EUCentral1);

var request = new GetPreSignedUrlRequest
{
BucketName = "my-bucket",
Key = "invoices/inv-001.pdf",
Verb = HttpVerb.PUT,
ContentType = "application/pdf",
Expires = DateTime.UtcNow.AddMinutes(5),
};

string presignedUrl = s3.GetPreSignedURL(request);
Console.WriteLine(presignedUrl);
# Requires the AWS CLI configured with credentials
aws s3 presign s3://my-bucket/invoices/inv-001.pdf \
--expires-in 300

PolyDoc API request

Pass the presigned URL in the cloudStorage field. PolyDoc converts the document and uploads the result directly to S3. The response is JSON containing the permanent object URL.
const response = await fetch('https://api.polydoc.tech/pdf/convert', {
method: 'POST',
headers: {
'Authorization': 'Bearer <YOUR_API_KEY>',
'Content-Type': 'application/json',
'X-Sandbox': 'true',
},
body: JSON.stringify({
source: '<html><body><h1>Hello</h1></body></html>',
cloudStorage: { presignedUrl },
}),
});

const result = await response.json();
console.log(result.data.url);
// → https://my-bucket.s3.eu-central-1.amazonaws.com/invoices/inv-001.pdf
import requests

headers = {
'Authorization': 'Bearer <YOUR_API_KEY>',
'Content-Type': 'application/json',
'X-Sandbox': 'true',
}

data = {
'source': '<html><body><h1>Hello</h1></body></html>',
'cloudStorage': {'presignedUrl': presigned_url},
}

response = requests.post(
'https://api.polydoc.tech/pdf/convert',
headers=headers,
json=data,
)

result = response.json()
print(result['data']['url'])
# → https://my-bucket.s3.eu-central-1.amazonaws.com/invoices/inv-001.pdf
require 'net/http'
require 'uri'
require 'json'

uri = URI.parse('https://api.polydoc.tech/pdf/convert')
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer <YOUR_API_KEY>'
request['Content-Type'] = 'application/json'
request['X-Sandbox'] = 'true'
request.body = JSON.dump({
'source' => '<html><body><h1>Hello</h1></body></html>',
'cloudStorage' => { 'presignedUrl' => presigned_url },
})

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end

result = JSON.parse(response.body)
puts result['data']['url']
# → https://my-bucket.s3.eu-central-1.amazonaws.com/invoices/inv-001.pdf
package main

import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)

func main() {
payload := map[string]interface{}{
"source": "<html><body><h1>Hello</h1></body></html>",
"cloudStorage": map[string]string{
"presignedUrl": presignedUrl,
},
}
body, _ := json.Marshal(payload)

req, _ := http.NewRequest("POST", "https://api.polydoc.tech/pdf/convert", bytes.NewBuffer(body))
req.Header.Set("Authorization", "Bearer <YOUR_API_KEY>")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Sandbox", "true")

client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()

var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
fmt.Println(result["data"].(map[string]interface{})["url"])
// → https://my-bucket.s3.eu-central-1.amazonaws.com/invoices/inv-001.pdf
}
<?php
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.polydoc.tech/pdf/convert');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'source' => '<html><body><h1>Hello</h1></body></html>',
'cloudStorage' => ['presignedUrl' => $presignedUrl],
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer <YOUR_API_KEY>',
'Content-Type: application/json',
'X-Sandbox: true',
]);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response, true);
echo $result['data']['url'];
// → https://my-bucket.s3.eu-central-1.amazonaws.com/invoices/inv-001.pdf
use reqwest::Client;
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::new();

let res = client
.post("https://api.polydoc.tech/pdf/convert")
.header("Authorization", "Bearer <YOUR_API_KEY>")
.header("X-Sandbox", "true")
.json(&json!({
"source": "<html><body><h1>Hello</h1></body></html>",
"cloudStorage": { "presignedUrl": presigned_url }
}))
.send()
.await?;

let result: serde_json::Value = res.json().await?;
println!("{}", result["data"]["url"]);
// → https://my-bucket.s3.eu-central-1.amazonaws.com/invoices/inv-001.pdf

Ok(())
}
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpRequest.BodyPublishers;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Map;

public class Main {
public static void main(String[] args) throws Exception {
HttpClient client = HttpClient.newHttpClient();
ObjectMapper mapper = new ObjectMapper();

String body = mapper.writeValueAsString(Map.of(
"source", "<html><body><h1>Hello</h1></body></html>",
"cloudStorage", Map.of("presignedUrl", presignedUrl)
));

HttpRequest request = HttpRequest.newBuilder()
.uri(new URI("https://api.polydoc.tech/pdf/convert"))
.header("Authorization", "Bearer <YOUR_API_KEY>")
.header("Content-Type", "application/json")
.header("X-Sandbox", "true")
.POST(BodyPublishers.ofString(body))
.build();

HttpResponse<String> response = client.send(
request, HttpResponse.BodyHandlers.ofString()
);

var result = mapper.readValue(response.body(), Map.class);
System.out.println(((Map) result.get("data")).get("url"));
// → https://my-bucket.s3.eu-central-1.amazonaws.com/invoices/inv-001.pdf
}
}
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;

using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", "<YOUR_API_KEY>");
client.DefaultRequestHeaders.Add("X-Sandbox", "true");

var payload = JsonSerializer.Serialize(new {
source = "<html><body><h1>Hello</h1></body></html>",
cloudStorage = new { presignedUrl }
});

var content = new StringContent(payload, Encoding.UTF8, "application/json");
var response = await client.PostAsync("https://api.polydoc.tech/pdf/convert", content);
var json = await response.Content.ReadAsStringAsync();

using var doc = JsonDocument.Parse(json);
Console.WriteLine(doc.RootElement.GetProperty("data").GetProperty("url").GetString());
// → https://my-bucket.s3.eu-central-1.amazonaws.com/invoices/inv-001.pdf
curl --location \
--request POST 'https://api.polydoc.tech/pdf/convert' \
--header 'Authorization: Bearer <YOUR_API_KEY>' \
--header 'Content-Type: application/json' \
--header 'X-Sandbox: true' \
--data-raw '{
"source": "<html><body><h1>Hello</h1></body></html>",
"cloudStorage": {
"presignedUrl": "<PRESIGNED_URL>"
}
}'

# Response:
# {
# "success": true,
# "data": {
# "url": "https://my-bucket.s3.eu-central-1.amazonaws.com/invoices/inv-001.pdf"
# }
# }

Response

On success, PolyDoc returns the permanent object URL (presigned query parameters stripped).
{
"success": true,
"message": "PDF uploaded to cloud storage.",
"data": {
"url": "https://my-bucket.s3.eu-central-1.amazonaws.com/invoices/inv-001.pdf"
}
}

Tips