HTTP Requests (Microservice Pattern)
When CobaltPDF is deployed as a dedicated PDF rendering service (e.g. an Azure Function or ASP.NET Core minimal API), client applications need a way to describe what PDF to generate without installing the full CobaltPDF library — including Chromium — themselves.
The CobaltPDF.Requests companion NuGet package solves this cleanly:
[Web App / Client] [PDF Rendering Service]
CobaltPDF.Requests (~50 KB) HTTP POST ─▶ CobaltPDF (full library)
- PdfRequest - CobaltEngine
- PdfResponse - Chromium + Playwright
- No Chromium - ExecuteAsync maps request → fluent API
Installation
Client application (web app, mobile backend, other microservices):
dotnet add package CobaltPDF.Requests
Rendering service (Azure Function, ASP.NET Core app, worker):
dotnet add package CobaltPDF
# CobaltPDF.Requests is included automatically as a dependency
PdfRequest — Full JSON Schema
{
"url": "https://example.com/invoice/42",
"html": null,
"landscape": false,
"paperFormat": "A4",
"grayscale": false,
"printBackground": true,
"mediaType": "screen",
"margins": {
"top": "10mm",
"bottom": "10mm",
"left": "15mm",
"right": "15mm"
},
"header": "<div style='font-size:10px;text-align:center;width:100%'>My Company</div>",
"footer": "<div style='font-size:10px;text-align:center;width:100%'>Page <span class='pageNumber'></span> of <span class='totalPages'></span></div>",
"watermark": {
"html": "<div style='font-size:80px;color:red'>DRAFT</div>",
"rotation": -45,
"opacity": 0.2,
"vertical": "middle",
"horizontal": "center"
},
"waitStrategy": "networkIdle",
"waitTimeoutMs": 30000,
"userAgent": "Mozilla/5.0 (compatible; MyReportBot/1.0)",
"extraHeaders": {
"Authorization": "Bearer eyJhbGci...",
"Accept-Language": "en-GB,en;q=0.9"
},
"customJs": "document.querySelector('.no-print')?.remove();",
"bypassCsp": false,
"cookies": [
{
"name": "session",
"value": "abc123",
"domain": "example.com",
"path": "/",
"secure": true,
"httpOnly": false
}
],
"localStorage": { "theme": "dark" },
"sessionStorage": { "userId": "42" },
"lazyLoadPages": 3,
"lazyLoadDelayMs": 200,
"encryption": {
"userPassword": "open123",
"ownerPassword": null,
"allowPrinting": true,
"allowCopying": false,
"allowModification": false
},
"metadata": {
"title": "Invoice #42",
"author": "Billing System",
"subject": "Monthly Invoice",
"keywords": "invoice, billing, 2026",
"creator": "MyApp"
}
}
PdfRequest Property Reference
| Property | Type | Default | Description |
|---|---|---|---|
url |
string? |
— | URL to render. Mutually exclusive with html. |
html |
string? |
— | Raw HTML to render. Mutually exclusive with url. |
landscape |
bool |
false |
Landscape orientation. |
paperFormat |
string |
"A4" |
Paper size: "A4", "A3", "Letter", "Legal", "Tabloid". |
margins |
object | null (1 cm) |
Page margins as CSS length strings (e.g. "10mm", "1cm"). |
grayscale |
bool |
false |
Render in grayscale. |
printBackground |
bool |
true |
Include CSS background colours and images. |
mediaType |
string |
"screen" |
CSS media type: "screen" or "print". |
header |
string? |
— | HTML header template (all pages). |
footer |
string? |
— | HTML footer template (all pages). |
watermark |
object | — | Watermark overlay on every page. |
waitStrategy |
string |
"networkIdle" |
Render trigger (see table below). |
waitTimeoutMs |
int |
30000 |
Timeout for wait strategy in milliseconds. |
userAgent |
string? |
— | Browser User-Agent string. null uses Chromium's default. |
extraHeaders |
object | {} |
HTTP headers sent with every request (navigation and sub-resources). |
customJs |
string? |
— | JavaScript to execute before capture. |
bypassCsp |
bool |
false |
Bypass page Content Security Policy. |
cookies |
array | [] |
Browser cookies to inject before navigation. |
localStorage |
object | {} |
localStorage key/value pairs. |
sessionStorage |
object | {} |
sessionStorage key/value pairs. |
lazyLoadPages |
int? |
null |
Viewport-heights to scroll for lazy content. |
lazyLoadDelayMs |
int |
200 |
Delay between scroll steps (ms). |
encryption |
object | — | Password protection settings. |
metadata |
object | — | PDF document properties. |
waitStrategy Values
| Value | Behaviour |
|---|---|
"networkIdle" |
Wait until no network activity for 500 ms (default) |
"signal" |
Wait for window.cobaltNotifyRender() from page JavaScript |
"selector:#my-id" |
Wait until the CSS selector is visible |
"js:window.ready===true" |
Poll a JavaScript expression until truthy |
"delay:2000" |
Fixed delay in milliseconds |
Client Examples
C# — HttpClient
using CobaltPdf.Requests;
using System.Net.Http.Json;
var request = new PdfRequest
{
Url = "https://myapp.com/invoice/42",
PaperFormat = "A4",
Header = "<div style='font-size:10px;text-align:center;width:100%'>My Company</div>",
Footer = "<div style='font-size:10px;text-align:center;width:100%'>Page <span class='pageNumber'></span></div>",
Metadata = new() { Title = "Invoice #42", Author = "Billing System" },
Cookies = [new() { Name = "session", Value = "abc123", Domain = "myapp.com" }]
};
var httpResponse = await httpClient.PostAsJsonAsync("https://pdf-service/api/pdf", request);
httpResponse.EnsureSuccessStatusCode();
// Option A — receive raw PDF bytes directly
var pdfBytes = await httpResponse.Content.ReadAsByteArrayAsync();
// Option B — receive PdfResponse JSON (includes metadata)
var pdfResponse = await httpResponse.Content.ReadFromJsonAsync<PdfResponse>();
byte[] pdfBytes2 = pdfResponse!.ToBytes();
JavaScript / TypeScript fetch
const request = {
url: "https://myapp.com/invoice/42",
paperFormat: "A4",
landscape: false,
metadata: { title: "Invoice #42" }
};
const response = await fetch("https://pdf-service/api/pdf", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(request)
});
const pdfBlob = await response.blob();
const url = URL.createObjectURL(pdfBlob);
window.open(url); // open in browser tab
Server Examples
Azure Function (Isolated Worker)
using CobaltPdf;
using CobaltPdf.Configuration;
using CobaltPdf.Requests;
using CobaltPdf.Extensions;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using System.Net;
public class PdfFunction
{
private readonly CobaltEngine _renderer;
public PdfFunction(CobaltEngine renderer) => _renderer = renderer;
[Function("GeneratePdf")]
public async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = "pdf")]
HttpRequestData req,
CancellationToken cancellationToken)
{
var request = await req.ReadFromJsonAsync<PdfRequest>();
if (request is null)
{
var bad = req.CreateResponse(HttpStatusCode.BadRequest);
await bad.WriteStringAsync("Invalid request body.");
return bad;
}
var pdf = await request.ExecuteAsync(_renderer, cancellationToken);
// Option A — return raw PDF bytes
var response = req.CreateResponse(HttpStatusCode.OK);
response.Headers.Add("Content-Type", "application/pdf");
response.Headers.Add("Content-Disposition", "attachment; filename=output.pdf");
await response.Body.WriteAsync(pdf.BinaryData, cancellationToken);
return response;
}
}
Function startup (Program.cs):
using CobaltPdf;
using CobaltPdf.Configuration;
using CobaltPdf.Extensions;
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureServices(services =>
{
services.AddCobaltPdf(o => CloudEnvironment.ConfigureForAzure(o));
})
.Build();
CobaltEngine.SetLicense(Environment.GetEnvironmentVariable("COBALTPDF_LICENSE")!);
await CobaltEngine.PreWarmAsync();
await host.RunAsync();
ASP.NET Core Minimal API
using CobaltPdf;
using CobaltPdf.Configuration;
using CobaltPdf.Extensions;
using CobaltPdf.Requests;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCobaltPdf(o => CloudEnvironment.ConfigureForDocker(o));
var app = builder.Build();
CobaltEngine.SetLicense(app.Configuration["CobaltPdf:LicenseKey"]!);
await CobaltEngine.PreWarmAsync();
// POST /api/pdf → returns raw PDF bytes
app.MapPost("/api/pdf", async (PdfRequest request, CobaltEngine renderer, CancellationToken ct) =>
{
var pdf = await request.ExecuteAsync(renderer, ct);
return Results.File(pdf.BinaryData, "application/pdf", "output.pdf");
});
// POST /api/pdf/json → returns PdfResponse JSON
app.MapPost("/api/pdf/json", async (PdfRequest request, CobaltEngine renderer, CancellationToken ct) =>
{
var pdf = await request.ExecuteAsync(renderer, ct);
return Results.Ok(PdfResponse.FromBytes(pdf.BinaryData));
});
app.Run();
Returning the PDF
Your endpoint can respond in two ways depending on what's convenient for the caller:
Raw bytes (application/pdf)
Best for browser downloads, mobile apps, and direct file saves.
return Results.File(pdf.BinaryData, "application/pdf", "output.pdf");
JSON (PdfResponse)
Best when the caller is another service that needs to process the PDF programmatically. The PDF binary is Base64-encoded inside the JSON.
return Results.Ok(PdfResponse.FromBytes(pdf.BinaryData));
{
"data": "<base64-encoded PDF>",
"sizeBytes": 142857,
"renderedAt": "2026-02-20T10:30:00Z"
}
The client decodes it with:
var pdfResponse = await httpResponse.Content.ReadFromJsonAsync<PdfResponse>();
byte[] bytes = pdfResponse!.ToBytes();
Security Considerations
- Protect your PDF endpoint with authentication (
AuthorizationLevel.Functionfor Azure Functions, API key middleware or JWT for ASP.NET Core). - Never expose the endpoint publicly without authentication — it renders arbitrary URLs.
- Consider whitelisting allowed domains in your endpoint if you do not control all callers.
- Store your license key in environment variables or Azure Key Vault, never in source code.
Related
- CobaltPDF.Requests — full guide: all properties, Azure Function, AWS Lambda, ECS, and ASP.NET Core examples
- Deployment — step-by-step Azure Function and Docker setup
- Configuration — pool sizing and cloud presets
- Wait Strategies — controlling the render trigger
- Custom JavaScript —
window.cobaltNotifyRender()with the signal strategy