Skip to main content

.NET SDK

Paperwright.Sdk is the first-party .NET client for the Paperwright render API. It wraps the HTTP calls, the X-Api-Key header, and error handling so a render stays one line in your code.

Available on NuGet

Paperwright.Sdk 0.2.0 is published. Every API and signature on this page is taken from the shipped package.


Install​

dotnet add package Paperwright.Sdk

Authenticate​

The SDK uses the machine lane: a workspace API key sent as X-Api-Key. Create one at /api-keys in the app. Keep it server-side — never ship it in a browser or mobile app.

The default base URL is https://api.paperwright.dev, so you only pass a base URL when pointing at a local API.


Render a stored template​

The common case: you designed a template in the app, and now you feed it data.

using Paperwright.Sdk;

var client = new PaperwrightClient(apiKey);

byte[] pdf = await client.RenderTemplateAsync(
templateId, // Guid
new { customerName = "Acme", total = 1240.50 });

await File.WriteAllBytesAsync("invoice.pdf", pdf);

RenderTemplateAsync(Guid templateId, object? data, CancellationToken ct = default) returns the PDF as a byte[]. The object you pass as data is serialised and matched against your template's schema fields.

Render inline content​

For one-off or dynamically composed documents, skip the stored template and pass the designer's JSON document directly:

byte[] pdf = await client.RenderInlineAsync(templateContent, new { name = "World" });

Render in a tenant's brand colours​

If your product serves several tenants, render one template in each tenant's colours by passing a PaperwrightTheme. Each colour is #rgb or #rrggbb; leave one null to keep the template's own. A template that doesn't use brand colours ignores the theme. See Rendering → Brand colours for the rules.

var theme = new PaperwrightTheme(Primary: "#E11D48", Accent: "#0EA5E9");

byte[] pdf = await client.RenderTemplateAsync(templateId, data, theme);

// or, for inline content:
byte[] inline = await client.RenderInlineAsync(templateContent, data, theme);

The overloads are RenderTemplateAsync(Guid templateId, object? data, PaperwrightTheme? theme, CancellationToken ct = default) and RenderInlineAsync(string content, object? data, PaperwrightTheme? theme, CancellationToken ct = default). The overloads without a theme are unchanged. A malformed colour or an unknown key comes back as a PaperwrightException with status 400.


Use it with dependency injection​

In a long-running app, register the client so it rides a pooled, factory-managed HttpClient instead of constructing its own:

// Program.cs
builder.Services.AddPaperwright(o =>
{
o.ApiKey = builder.Configuration["Paperwright:ApiKey"]!;
// o.BaseUrl = "http://localhost:5110"; // optional; defaults to production
});

Then inject it wherever you need it:

public class InvoiceService(PaperwrightClient paperwright)
{
public Task<byte[]> GenerateAsync(Invoice invoice, CancellationToken ct) =>
paperwright.RenderTemplateAsync(
invoice.TemplateId,
new { invoiceNumber = invoice.Number, total = invoice.Total },
ct);
}

AddPaperwright throws at startup if ApiKey is unset, so a misconfigured deployment fails fast rather than at the first render.


Error handling​

Failed renders throw PaperwrightException:

try
{
var pdf = await client.RenderTemplateAsync(templateId, data, ct);
}
catch (PaperwrightException ex)
{
logger.LogError("Render failed: {Message}", ex.Message);
}

See the API reference overview for what each status code means — in particular 402 (quota exceeded) and 409 (a concurrent edit changed the template).


Not using .NET?​

The API is plain HTTPS and the OpenAPI document is public, so any language works.

curl -X POST https://api.paperwright.dev/api/templates/<TEMPLATE_ID>/render \
-H "X-Api-Key: <YOUR_KEY>" \
-H "Content-Type: application/json" \
-d '{ "data": { "customerName": "Acme", "total": 1240.50 } }' \
--output invoice.pdf

Set the generated client's base URL to https://api.paperwright.dev and send X-Api-Key on every request.


Token grammar​

Tokens in template content resolve at render time. The full grammar lives in API Reference → Rendering; the common forms are:

TokenResolves to
{{field}}Value from data.field
{{field | number:"#,##0.00"}}Formatted number (pattern or preset)
{{field | date:"medium"}}Formatted date (preset)
{{field | date:"dd/MM/yyyy"}}Formatted date (custom format string)
{{$today | date:"long"}}Server date at render time
{{$now | datetime:"24h"}}Server datetime at render time
{{$page}} / {{$pages}}Page number / total pages

Feedback​

Found a rough edge in the SDK? Come tell us in Discord.