Skip to main content

Wasabi

Prerequisites

  • A PolyDoc account and API key — sign up for free, no credit card required. The free plan includes 150 PDF conversions per month.
  • A Wasabi account with a bucket. Create a bucket
  • An access key pair with write access to the target bucket.

Integration

Generate a presigned URL

Wasabi is fully S3-compatible, so you can use the AWS SDK with Wasabi's endpoint. No special SDK is needed.
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",
endpoint: "https://s3.eu-central-1.wasabisys.com",
credentials: {
accessKeyId: process.env.WASABI_ACCESS_KEY,
secretAccessKey: process.env.WASABI_SECRET_KEY,
},
});

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

# pip install boto3

s3 = boto3.client(
"s3",
region_name="eu-central-1",
endpoint_url="https://s3.eu-central-1.wasabisys.com",
aws_access_key_id=os.environ["WASABI_ACCESS_KEY"],
aws_secret_access_key=os.environ["WASABI_SECRET_KEY"],
)

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

# gem install aws-sdk-s3

s3 = Aws::S3::Resource.new(
region: "eu-central-1",
endpoint: "https://s3.eu-central-1.wasabisys.com",
credentials: Aws::Credentials.new(
ENV["WASABI_ACCESS_KEY"],
ENV["WASABI_SECRET_KEY"]
)
)
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,
content_type: "application/pdf"
)
package main

import (
"context"
"fmt"
"os"
"time"

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

func main() {
cfg, _ := config.LoadDefaultConfig(context.TODO(),
config.WithRegion("eu-central-1"),
config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(
os.Getenv("WASABI_ACCESS_KEY"), os.Getenv("WASABI_SECRET_KEY"), "",
)),
)
client := s3.NewFromConfig(cfg, func(o *s3.Options) {
o.BaseEndpoint = aws.String("https://s3.eu-central-1.wasabisys.com")
})
presignClient := s3.NewPresignClient(client)

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

fmt.Println(req.URL)
}
<?php
require 'vendor/autoload.php';

// composer require aws/aws-sdk-php

use Aws\S3\S3Client;

$s3 = new S3Client([
'version' => 'latest',
'region' => 'eu-central-1',
'endpoint' => 'https://s3.eu-central-1.wasabisys.com',
'credentials' => [
'key' => getenv('WASABI_ACCESS_KEY'),
'secret' => getenv('WASABI_SECRET_KEY'),
],
]);

$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::{
config::{Builder, Credentials, Region},
presigning::PresigningConfig,
Client,
};
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let creds = Credentials::new(
std::env::var("WASABI_ACCESS_KEY")?,
std::env::var("WASABI_SECRET_KEY")?,
None, None, "wasabi",
);
let config = Builder::new()
.behavior_version(BehaviorVersion::latest())
.region(Region::new("eu-central-1"))
.endpoint_url("https://s3.eu-central-1.wasabisys.com")
.credentials_provider(creds)
.build();
let client = Client::from_conf(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.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
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.net.URI;
import java.time.Duration;

public class Main {
public static void main(String[] args) {
try (S3Presigner presigner = S3Presigner.builder()
.region(Region.of("eu-central-1"))
.endpointOverride(URI.create("https://s3.eu-central-1.wasabisys.com"))
.credentialsProvider(StaticCredentialsProvider.create(
AwsBasicCredentials.create(
System.getenv("WASABI_ACCESS_KEY"),
System.getenv("WASABI_SECRET_KEY")
)
))
.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();

System.out.println(presigner.presignPutObject(presignRequest).url());
}
}
}
using Amazon;
using Amazon.S3;
using Amazon.S3.Model;
using Amazon.Runtime;

// NuGet: AWSSDK.S3

var credentials = new BasicAWSCredentials(
Environment.GetEnvironmentVariable("WASABI_ACCESS_KEY"),
Environment.GetEnvironmentVariable("WASABI_SECRET_KEY")
);

var config = new AmazonS3Config
{
ServiceURL = "https://s3.eu-central-1.wasabisys.com",
ForcePathStyle = true,
};

var s3 = new AmazonS3Client(credentials, config);

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 Wasabi credentials:
# aws configure --profile wasabi
# AWS Access Key ID: <WASABI_ACCESS_KEY>
# AWS Secret Access Key: <WASABI_SECRET_KEY>
# Default region: eu-central-1

aws s3 presign s3://my-bucket/invoices/inv-001.pdf \
--expires-in 300 \
--endpoint-url https://s3.eu-central-1.wasabisys.com \
--profile wasabi

PolyDoc API request

Pass the presigned URL to PolyDoc. The converted document is uploaded directly to your Wasabi bucket. 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.wasabisys.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.wasabisys.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.wasabisys.com/invoices/inv-001.pdf
package main

import (
"bytes"
"encoding/json"
"fmt"
"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.wasabisys.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.wasabisys.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.wasabisys.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.wasabisys.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.wasabisys.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.
{
"success": true,
"message": "PDF uploaded to cloud storage.",
"data": {
"url": "https://my-bucket.s3.eu-central-1.wasabisys.com/invoices/inv-001.pdf"
}
}

Tips