Table of Contents

CobaltPDF.Requests

CobaltPDF.Requests is a lightweight companion NuGet package containing the serialisable request and response models used when CobaltPDF is deployed as a PDF rendering microservice.

Install it in client applications — web APIs, mobile backends, other microservices — that need to send PDF generation requests over HTTP without pulling in Chromium or Playwright themselves.

[Client Application]                        [PDF Rendering Service]
CobaltPDF.Requests (~50 KB)  HTTP POST ──▶  CobaltPDF (full library)
  PdfRequest (JSON)                           CobaltEngine + Chromium
  PdfResponse (JSON)                          request.ExecuteAsync(engine)
  No Chromium, No Playwright

Installation

Client (web app, mobile backend, other service):

dotnet add package CobaltPDF.Requests

Rendering service (Azure Function, ASP.NET Core, AWS Lambda, worker):

dotnet add package CobaltPDF
# CobaltPDF.Requests models are included automatically

Core Types

PdfRequest

Describes the PDF to generate. All properties are optional except either Url or Html.

Property Type Default Description
Url string? URL to render. Mutually exclusive with Html.
Html string? Raw HTML string to render. Mutually exclusive with Url.
Landscape bool false Landscape orientation.
PaperFormat string "A4" Paper size: "A4", "A3", "Letter", "Legal", "Tabloid".
Margins PdfRequestMargins? 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 printed on every page.
Footer string? HTML footer template printed on every page.
Watermark PdfRequestWatermark? Watermark overlay applied to every page.
WaitStrategy string "networkIdle" When to trigger the PDF capture (see below).
WaitTimeoutMs int 30000 Timeout for the wait strategy in milliseconds.
UserAgent string? Browser User-Agent string. null uses Chromium's default.
ExtraHeaders Dictionary<string,string> {} HTTP headers sent with every request (navigation and sub-resources).
CustomJs string? JavaScript executed on the page before capture.
BypassCsp bool false Bypass the page's Content Security Policy.
Cookies List<PdfRequestCookie> [] Browser cookies injected before navigation.
LocalStorage Dictionary<string,string> {} localStorage values written before the page loads.
SessionStorage Dictionary<string,string> {} sessionStorage values written before the page loads.
LazyLoadPages int? null Viewport-heights to scroll to trigger lazy-loaded content.
LazyLoadDelayMs int 200 Delay between scroll steps in milliseconds.
Encryption PdfRequestEncryption? Password protection and permission settings.
Metadata PdfRequestMetadata? Title, author, subject, keywords embedded in the PDF.

WaitStrategy Values

Value Behaviour
"networkIdle" Wait until no network activity for 500 ms (default)
"signal" Wait for window.cobaltNotifyRender() called from page JavaScript
"selector:#my-id" Wait until the CSS selector is present and visible
"js:window.ready===true" Poll a JavaScript expression until it is truthy
"delay:2000" Wait a fixed number of milliseconds

PdfResponse

Returned by the rendering service after a successful render.

Property Type Description
Data string Base64-encoded PDF binary.
SizeBytes long Size of the PDF in bytes.
RenderedAt DateTime UTC timestamp of when the render completed.

Helper methods:

byte[]       bytes  = response.ToBytes();   // decode to raw bytes
MemoryStream stream = response.ToStream();  // decode to a MemoryStream

Factory:

// On the server, wrap raw bytes into a PdfResponse for JSON transport
var response = PdfResponse.FromBytes(pdf.BinaryData);

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;font-family:sans-serif;'>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 }
  ],

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

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> of <span class='totalPages'></span></div>",
    Metadata    = new() { Title = "Invoice #42", Author = "Billing System" },
    Encryption  = new() { UserPassword = "open123", AllowPrinting = true },
    Cookies     = [new() { Name = "session", Value = "abc123", Domain = "myapp.com" }]
};

var httpResponse = await httpClient.PostAsJsonAsync("https://pdf-service/api/pdf", request);
httpResponse.EnsureSuccessStatusCode();

// Option A — raw PDF bytes (Content-Type: application/pdf)
byte[] pdfBytes = await httpResponse.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync("invoice.pdf", pdfBytes);

// Option B — PdfResponse JSON (Content-Type: application/json)
var pdfResponse = await httpResponse.Content.ReadFromJsonAsync<PdfResponse>();
byte[] pdfBytes2 = pdfResponse!.ToBytes();

TypeScript / JavaScript — fetch

const request = {
  url:         "https://myapp.com/invoice/42",
  paperFormat: "A4",
  landscape:   false,
  metadata:    { title: "Invoice #42", author: "Billing System" }
};

// Raw PDF response
const response = await fetch("https://pdf-service/api/pdf", {
  method:  "POST",
  headers: { "Content-Type": "application/json" },
  body:    JSON.stringify(request)
});

// Stream directly into a browser download
const blob = await response.blob();
const url  = URL.createObjectURL(blob);
const a    = document.createElement("a");
a.href     = url;
a.download = "invoice.pdf";
a.click();

Python — requests

import requests

payload = {
    "url":         "https://myapp.com/invoice/42",
    "paperFormat": "A4",
    "landscape":   False,
    "metadata":    { "title": "Invoice #42", "author": "Billing System" }
}

response = requests.post("https://pdf-service/api/pdf", json=payload)
response.raise_for_status()

with open("invoice.pdf", "wb") as f:
    f.write(response.content)

Server Examples

Azure Function (Isolated Worker — .NET 8)

This is the recommended pattern for Azure. The function registers CobaltEngine via DI so the browser pool survives across invocations on a Premium Plan instance.

Warning

The Consumption Plan is not supported. Use the Premium Plan (EP1 or higher) or the Dedicated (App Service) Plan so the process is not torn down between requests.

Program.cs — host configuration:

using CobaltPdf;
using CobaltPdf.Configuration;
using CobaltPdf.Extensions;
using Microsoft.Extensions.Hosting;

var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults()
    .ConfigureServices(services =>
    {
        // Registers CobaltEngine as a singleton with the Azure cloud preset
        services.AddCobaltPdf(o => CloudEnvironment.ConfigureForAzure(o));
    })
    .Build();

// Activate license and pre-warm the browser pool before accepting requests
CobaltEngine.SetLicense(Environment.GetEnvironmentVariable("COBALTPDF_LICENSE")!);
await CobaltEngine.PreWarmAsync();

await host.RunAsync();

PdfFunction.cs — the HTTP-triggered function:

using CobaltPdf;
using CobaltPdf.Requests;
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 or missing request body.");
            return bad;
        }

        // ExecuteAsync maps every PdfRequest property to the CobaltEngine fluent API
        var pdf = await request.ExecuteAsync(_renderer, cancellationToken);

        // 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;
    }

    // Optional: return PdfResponse JSON instead for service-to-service calls
    [Function("GeneratePdfJson")]
    public async Task<HttpResponseData> RunJson(
        [HttpTrigger(AuthorizationLevel.Function, "post", Route = "pdf/json")]
        HttpRequestData req,
        CancellationToken cancellationToken)
    {
        var request = await req.ReadFromJsonAsync<PdfRequest>();
        if (request is null)
        {
            var bad = req.CreateResponse(HttpStatusCode.BadRequest);
            await bad.WriteStringAsync("Invalid or missing request body.");
            return bad;
        }

        var pdf      = await request.ExecuteAsync(_renderer, cancellationToken);
        var response = req.CreateResponse(HttpStatusCode.OK);
        await response.WriteAsJsonAsync(PdfResponse.FromBytes(pdf.BinaryData), cancellationToken);
        return response;
    }
}

Dockerfile for Linux Azure Functions:

FROM mcr.microsoft.com/azure-functions/dotnet-isolated:4-dotnet-isolated8.0 AS base

# Chromium system library dependencies
RUN apt-get update && apt-get install -y \
    libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 \
    libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 \
    libxfixes3 libxrandr2 libgbm1 libasound2 \
    --no-install-recommends && rm -rf /var/lib/apt/lists/*

WORKDIR /home/site/wwwroot
COPY ./publish .

Publish and deploy:

dotnet publish -c Release -r linux-x64 --self-contained false -o ./publish

az functionapp deployment source config-zip \
    --resource-group myRG \
    --name my-pdf-function \
    --src publish.zip

Set the license key as an Application Setting:

az functionapp config appsettings set \
    --resource-group myRG \
    --name my-pdf-function \
    --settings COBALTPDF_LICENSE="YOUR-LICENSE-KEY"

AWS Lambda (Custom Container Runtime)

Lambda's stateless execution model means the browser pool may be recreated on each cold start. For sustained throughput, prefer ECS / Fargate instead.

Warning

Lambda imposes a 512 MB /tmp limit and does not guarantee process reuse between invocations. Use ConfigureForLowMemory to set MaxSize = 1 and enable --single-process mode.

LambdaEntryPoint.cs:

using Amazon.Lambda.AspNetCoreServer;

namespace MyPdfService;

public class LambdaEntryPoint : APIGatewayProxyFunction
{
    protected override void Init(IWebHostBuilder builder)
        => builder.UseStartup<Startup>();
}

Startup.cs (or Program.cs with minimal hosting):

using CobaltPdf;
using CobaltPdf.Configuration;
using CobaltPdf.Extensions;
using CobaltPdf.Requests;

var builder = WebApplication.CreateBuilder(args);

// Low-memory preset: MaxSize=1, --single-process, suitable for Lambda
builder.Services.AddCobaltPdf(o => CloudEnvironment.ConfigureForLowMemory(o));

var app = builder.Build();

CobaltEngine.SetLicense(Environment.GetEnvironmentVariable("COBALTPDF_LICENSE")!);
await CobaltEngine.PreWarmAsync();

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");
});

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();

Dockerfile for AWS Lambda:

FROM public.ecr.aws/lambda/dotnet:8 AS base

# Chromium system library dependencies (Amazon Linux 2023)
RUN yum install -y \
    nss atk at-spi2-atk cups-libs libdrm \
    libxkbcommon libXcomposite libXdamage \
    libXfixes libXrandr mesa-libgbm alsa-lib \
    && yum clean all

WORKDIR /var/task
COPY ./publish .
CMD ["MyPdfService::MyPdfService.LambdaEntryPoint::FunctionHandlerAsync"]

Recommended Lambda configuration:

Setting Value
Memory 2048 MB
Timeout 60 seconds
Architecture x86_64

Publish and push:

dotnet publish -c Release -r linux-x64 --self-contained false -o ./publish

docker build -t my-pdf-lambda .
docker tag my-pdf-lambda:latest <account>.dkr.ecr.<region>.amazonaws.com/my-pdf-lambda:latest
docker push <account>.dkr.ecr.<region>.amazonaws.com/my-pdf-lambda:latest

Set the license key via AWS Secrets Manager or an environment variable in your function configuration.


AWS ECS / Fargate

ECS is the recommended AWS deployment for sustained PDF workloads. The long-running container keeps the browser pool alive across requests.

Program.cs:

using CobaltPdf;
using CobaltPdf.Configuration;
using CobaltPdf.Extensions;
using CobaltPdf.Requests;

var builder = WebApplication.CreateBuilder(args);

// ECS preset: omits --single-process for better stability under sustained load
builder.Services.AddCobaltPdf(o =>
{
    CloudEnvironment.ConfigureForAwsEcs(o);
    o.MaxSize = Math.Max(2, Environment.ProcessorCount);
});

var app = builder.Build();

CobaltEngine.SetLicense(Environment.GetEnvironmentVariable("COBALTPDF_LICENSE")!);
await CobaltEngine.PreWarmAsync();

var lifetime = app.Services.GetRequiredService<IHostApplicationLifetime>();
lifetime.ApplicationStopping.Register(() =>
    CobaltEngine.ShutdownAsync().GetAwaiter().GetResult());

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");
});

app.Run();

ECS Task Definition recommendations:

Resource Minimum Recommended
vCPU 0.5 1.0
Memory 1 GB 2 GB
linuxParameters.sharedMemorySize 256 MB

Set the license key as an ECS environment variable or retrieve it from AWS Secrets Manager:

{
  "environment": [
    { "name": "COBALTPDF_LICENSE", "value": "YOUR-LICENSE-KEY" }
  ]
}

ASP.NET Core Minimal API

The simplest self-hosted rendering service — suitable for Docker, Azure App Service, or a Linux VM.

using CobaltPdf;
using CobaltPdf.Configuration;
using CobaltPdf.Extensions;
using CobaltPdf.Requests;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCobaltPdf(o =>
{
    CloudEnvironment.ConfigureForDocker(o);
    o.MaxSize = Math.Max(2, Environment.ProcessorCount);
});

var app = builder.Build();

CobaltEngine.SetLicense(app.Configuration["CobaltPdf:LicenseKey"]!);
await CobaltEngine.PreWarmAsync();

// Graceful shutdown — closes browser pool cleanly
var lifetime = app.Services.GetRequiredService<IHostApplicationLifetime>();
lifetime.ApplicationStopping.Register(() =>
    CobaltEngine.ShutdownAsync().GetAwaiter().GetResult());

// POST /api/pdf → 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 → PdfResponse JSON (for service-to-service calls)
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:

Raw bytes — application/pdf

Best for browser downloads, mobile apps, and direct file saves.

return Results.File(pdf.BinaryData, "application/pdf", "output.pdf");

The client reads the response body directly as bytes — no JSON parsing required.

JSON — PdfResponse

Best for service-to-service calls where the caller needs to process or store the PDF programmatically. The binary is Base64-encoded inside the JSON envelope.

return Results.Ok(PdfResponse.FromBytes(pdf.BinaryData));
{
  "data":       "<base64-encoded PDF bytes>",
  "sizeBytes":  142857,
  "renderedAt": "2026-02-20T10:30:00Z"
}

Decode on the client:

var pdfResponse = await httpResponse.Content.ReadFromJsonAsync<PdfResponse>();
byte[] bytes = pdfResponse!.ToBytes();

Platform Summary

Platform Package on server Preset Notes
Azure Functions Premium CobaltPDF ConfigureForAzure Do not use Consumption Plan
Azure App Service (Linux) CobaltPDF ConfigureForAzure Set license in App Settings
Azure Container Apps CobaltPDF ConfigureForDocker Standard Docker container
AWS ECS / Fargate CobaltPDF ConfigureForAwsEcs Recommended for sustained load
AWS Lambda CobaltPDF ConfigureForLowMemory Custom container; pool may restart
ASP.NET Core (Docker) CobaltPDF ConfigureForDocker Full control, any host

Security Considerations

  • Authenticate every endpoint. The rendering service has access to any URL reachable from its network. An unauthenticated endpoint is a significant security risk.
    • Azure Functions: use AuthorizationLevel.Function (function key in the request header).
    • ASP.NET Core: add JWT bearer auth or API key middleware.
  • Whitelist allowed domains if you do not control all callers, to prevent SSRF (Server-Side Request Forgery).
  • Store license keys in secrets, not in source code — use Azure Key Vault, AWS Secrets Manager, or environment variables.
  • Do not expose html rendering publicly without sanitisation — arbitrary HTML rendering can be used to render internal URLs.