Advanced Features
Working Examples
Webhooks
All request types support asynchronous webhook processing. Gotenberg generates the PDF and POSTs it to your webhook URL.
var builder = new UrlRequestBuilder()
.SetUrl("https://www.example.com")
.ConfigureRequest(req =>
{
req.AddWebhook(hook =>
{
hook.SetUrl("http://host.docker.internal:5000/api/webhook/pdf")
.SetErrorUrl("http://host.docker.internal:5000/api/webhook/error")
.AddExtraHeader("custom-header", "value");
});
req.SetPageRanges("1-2");
})
.WithPageProperties(pp => pp.SetPaperSize(PaperSizes.A4));
await sharpClient.FireWebhookAndForgetAsync(builder);
PDF Output Options
Available on all request types via SetPdfOutputOptions():
| Method | Description |
|---|---|
SetPdfFormat(PdfFormat.A2b) |
Convert to PDF/A (A1b, A2b, A3b) |
SetPdfUa() |
Enable PDF/UA accessibility |
SetFlatten() |
Flatten form fields |
SetGenerateTaggedPdf() |
Embed logical structure tags |
SetMetadata(dict) |
Write XMP metadata |
SetEncryption(user, owner) |
Password-protect the PDF |
DDD Value Objects
The client uses validated value objects to prevent invalid state at construction time. Invalid values throw immediately rather than failing at the Gotenberg API level.
| Value Object | Constraint | Example |
|---|---|---|
CssSelector |
Non-empty string | CssSelector.Create("#app") |
GotenbergStatusCode |
100-599 | GotenbergStatusCode.Create(499) |
DomainName |
Non-empty, trimmed | DomainName.Create("cdn.example.com") |
EmulatedMediaFeature |
Non-empty name + value | EmulatedMediaFeature.PrefersColorScheme("dark") |
PdfPassword |
Non-empty (redacted ToString) | PdfPassword.Create("secret") |
ImageQuality |
1-100 | ImageQuality.Create(85) |
ImageResolution |
75/150/300/600/1200 DPI | ImageResolution.Dpi300 |
RotationAngle |
90/180/270 | RotationAngle.Degrees90 |
PageRanges |
Validated format "1-3,5" | PageRanges.Create("1-3,5,8-10") |
ScreenDimension |
Positive integer | ScreenDimension.Create(1920) |
CompressionQuality |
0-100 | CompressionQuality.Create(90) |
Merge Multiple URLs
Build a multi-page PDF by merging the front pages of multiple websites:
var sites = new[] { "https://example.com", "https://example.org" }
.Select(u => new Uri(u));
var tasks = sites.Select(uri =>
{
var builder = new UrlRequestBuilder()
.SetUrl(uri)
.ConfigureRequest(r => r.SetPageRanges("1-2"))
.WithPageProperties(pp => pp.UseChromeDefaults().SetLandscape());
return sharpClient.UrlToPdfAsync(builder);
});
var pdfs = await Task.WhenAll(tasks);
var mergeBuilder = new MergeBuilder()
.WithAssets(b => b.AddItems(
pdfs.Select((s, i) => KeyValuePair.Create($"{i}.pdf", s))));
var merged = await sharpClient.MergePdfsAsync(mergeBuilder);
Request Configuration
All builders support ConfigureRequest() for cross-cutting request settings:
builder.ConfigureRequest(config =>
{
config.SetTrace("my-request-id") // Custom trace ID for logs
.SetPageRanges("1-5") // Page range selection
.SetResultFileName("output.pdf"); // Output filename
});
Version Compatibility
Some Gotenberg routes only exist in newer releases. Requests that use one declare the release that
introduced it, and the client checks it against the running service before sending — so an
unsupported feature surfaces as an explanatory error instead of an opaque 404:
// Against Gotenberg 8.20:
await sharpClient.ExecutePdfEngineAsync(
PdfEngineBuilders.WriteBookmarks(b => b.Add("Intro", 1))
.WithPdfs(a => a.AddItem("doc.pdf", pdfBytes)));
// GotenbergVersionNotSupportedException:
// Writing PDF bookmarks requires Gotenberg 8.28.0 or newer, but the service at this
// address reports 8.20.0. Upgrade Gotenberg to use this feature.
The version is fetched from /version once per client instance and cached. Routes available in
every supported release are never checked, so most requests cost no extra round-trip.
Currently gated:
| Feature | Requires |
|---|---|
Split (PdfEngineBuilders.Split) |
8.15.0 |
Flatten (PdfEngineBuilders.Flatten) |
8.16.0 |
Embedded files (PdfEngineBuilders.Embed) |
8.25.0 |
Bookmarks (ReadBookmarks / WriteBookmarks) |
8.28.0 |
Rotate, metadata, watermark, stamp, merge and the screenshot routes all predate Gotenberg 8.5 and are not gated.
Checking ahead of time
To branch on support rather than catch an exception:
var request = PdfEngineBuilders.ReadBookmarks()
.WithPdfs(a => a.AddItem("doc.pdf", pdfBytes))
.Build();
if (await sharpClient.SupportsAsync(request))
outlines = await sharpClient.ReadPdfBookmarksAsync(request);
EnsureSupportedAsync(request) does the same but throws, and GetGotenbergVersionAsync() returns
the parsed, cached GotenbergVersion directly.
Services that don't report a version
Gotenberg releases predating the /version route report nothing at all. Those releases are older
than every version this client gates on, so an undeterminable version is treated as unsupported.
If your service does support the feature but hides /version — behind a proxy, for instance — turn
the check off:
Declaring a minimum on your own requests
Custom requests deriving from the built-in request types can declare their own floor with
[MinimumGotenbergVersion]:
[MinimumGotenbergVersion("8.28.0", Feature = "My custom route")]
public sealed class MyCustomRequest : PdfEngineRequest { /* ... */ }
Examples
See the examples folder for complete working console applications demonstrating each feature.