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:PutObjectpermission 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"
}
}