polipage

package module
v0.11.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 30, 2026 License: MIT Imports: 18 Imported by: 0

README

Poli Page SDK for Go

Release Ci Codeql Coverage Go Lint Go.dev Report Deps Docs License

Official Go SDK for Poli Page — render polished PDFs from HTML templates via the Poli Page API.

→ Documentation: https://poli-page.github.io/sdk-go/

→ API reference (auto-generated from doc comments): pkg.go.dev/github.com/poli-page/sdk-go

Install

go get github.com/poli-page/sdk-go

Requires Go 1.25 or later. Zero runtime dependencies.

Quick start

Render the welcome template
package main

import (
    "context"
    "log"
    "os"

    polipage "github.com/poli-page/sdk-go"
    "github.com/poli-page/sdk-go/option"
)

func main() {
    client := polipage.NewClient(option.WithAPIKey(os.Getenv("POLI_PAGE_API_KEY")))

    pdf, err := client.Render.PDF(context.Background(), polipage.RenderInput{
        Project:  "getting-started",
        Template: "welcome",
        Data:     map[string]any{"name": "SDK Demo"},
    })
    if err != nil {
        log.Fatal(err)
    }
    if err := os.WriteFile("welcome.pdf", pdf, 0o644); err != nil {
        log.Fatal(err)
    }
}

Run it with a sandbox key: POLI_PAGE_API_KEY=pp_sandbox_x go run . (replace pp_sandbox_x with your key). Every organization gets a getting-started project with a welcome template, published into its sandbox environment when the organization is created, so the snippet runs as-is on a fresh organization. getting-started is a normal, editable project: if your organization renamed it, deleted it or removed it from sandbox, the API answers 404 PROJECT_NOT_FOUND or 404 PROJECT_NOT_IN_ENVIRONMENT. Use a fresh organization in that case.

A render uses the project's copy published in the API key's environment. For your own templates, publish the project to that environment, then swap the slugs:

pdf, err := client.Render.PDF(ctx, polipage.RenderInput{
    Project:  "billing",
    Template: "invoice",
    Data:     map[string]any{"invoiceNumber": "INV-001", "total": 1280},
})

To try a template or raw HTML without an API key, use the playground at play.poli.page.

Write a PDF to disk
err := polipage.RenderToFile(ctx, client, polipage.RenderInput{
    Project:  "getting-started",
    Template: "welcome",
    Data:     map[string]any{"name": "SDK Demo"},
}, "./welcome.pdf")

RenderToFile streams response bytes directly to disk (bounded memory). Parent directories are created on demand; existing files are overwritten.

Try it locally — runnable demo

The repo ships a runnable demo that exercises every public method end-to-end against the API:

go run ./cmd/demo
# or remote-only, no checkout:
go run github.com/poli-page/sdk-go/cmd/demo@latest

The demo reads POLI_PAGE_API_KEY and the optional POLI_PAGE_BASE_URL from the shell or ./.env. With no key, it asks for a sandbox key (pp_sandbox_…), checks its format and saves it to .env, so later runs are silent. It renders getting-started/welcome and writes its output to ./output/, in nine steps:

  1. Render.PDF
  2. Render.PDFStream
  3. RenderToFile
  4. Render.Document
  5. Documents.Get
  6. Documents.Thumbnails (the demo carries on if the plan does not include thumbnails)
  7. Documents.Preview
  8. Documents.Delete
  9. not-found: renders template does-not-exist with empty data. The API answers 404 TEMPLATE_NOT_FOUND, and the demo prints the error's code, status, message and request id. Any other result (a success, a network error, another API error) fails the demo.
Stream — for large PDFs or piping to S3 / HTTP responses
body, err := client.Render.PDFStream(ctx, polipage.RenderInput{
    Project:  "billing",
    Template: "invoice",
    Data:     map[string]any{"invoiceNumber": "INV-001"},
})
if err != nil {
    return err
}
defer body.Close()

// Pipe straight to an HTTP response, an S3 upload, a file — anything that
// accepts io.Reader. Memory stays bounded regardless of document size.
_, err = io.Copy(w, body)

Working with stored documents

Every render produces a stored document, accessible via documentId for later download or thumbnails. Render.PDF and Render.PDFStream are conveniences that chain a presigned-URL fetch internally to return bytes; Render.Document returns just the descriptor (skip the auto-download when you'll fetch the bytes later).

// 1. Render and store
doc, err := client.Render.Document(ctx, polipage.RenderInput{
    Project:  "billing",
    Template: "invoice",
    Data:     map[string]any{"invoiceNumber": "INV-001"},
    Metadata: polipage.RenderMetadata{"customerId": "cust_123"}, // your own audit data
})
// doc.DocumentID, doc.Environment, doc.PageCount, doc.SizeBytes, doc.PresignedPDFURL, doc.Metadata, ...

// 2. Save doc.DocumentID in your database
_ = db.Invoices.Update("INV-001", doc.DocumentID)

// 3. Later, fetch a fresh presigned URL + download
fresh, err := client.Documents.Get(ctx, doc.DocumentID)
pdf, err := fresh.DownloadPDF(ctx)

// 4. Generate thumbnails
thumbs, err := client.Documents.Thumbnails(ctx, doc.DocumentID, polipage.ThumbnailOptions{
    Width:  320,
    Format: polipage.ThumbnailFormatPNG,
})

// 5. When done, soft-delete (deleting it again also succeeds)
err = client.Documents.Delete(ctx, doc.DocumentID)

The presigned URL has a ~15-minute TTL. If DownloadPDF fails with Code: ErrCodeDownloadFailed (HTTP 403 from S3), call Documents.Get(ctx, id) to refresh and retry.

Authentication & environments

An API key belongs to one environment of your organization, and its text names that environment: pp_{env}_ followed by 32 lowercase hex characters. A full key matches:

^pp_(?=[a-z][a-z0-9-]{0,19}_)[a-z][a-z0-9]*(?:-[a-z0-9]+)*_[0-9a-f]{32}$
Environment Key Watermark Counted
sandbox (every org) pp_sandbox_… yes no
live (every org, deletable on Pro+) pp_live_… no yes
created on Pro+ (e.g. staging) pp_staging_… no yes

"Counted" means the render counts toward the organization's monthly PDF quota. sandbox documents are watermarked, are not counted and expire 30 days after creation. There is no watermark option: a template that wants a watermark outside sandbox uses the poli-watermark class.

A render uses the project's copy published in the key's environment, and DocumentDescriptor.Environment reports the environment a document was rendered in. Every key uses the same endpoint, https://api.poli.page. The SDK sends the key as a Bearer token and never inspects or validates it.

Methods

Method Returns Description
client.Render.PDF(ctx, in, opts...) ([]byte, error) Render a PDF, return bytes
client.Render.PDFStream(ctx, in, opts...) (io.ReadCloser, error) Render and stream the response
client.Render.Document(ctx, in, opts...) (*DocumentDescriptor, error) Render and return descriptor (skip auto-download)
client.Documents.Get(ctx, id, opts...) (*DocumentDescriptor, error) Retrieve a stored document
client.Documents.Preview(ctx, id, opts...) (*DocumentPreviewResult, error) Stored document's paginated HTML
client.Documents.Thumbnails(ctx, id, options, opts...) ([]Thumbnail, error) Page thumbnails (JPEG/WebP/PNG, base64)
client.Documents.Delete(ctx, id, opts...) error Soft-delete a stored document (a re-delete also succeeds)
(*DocumentDescriptor).DownloadPDF(ctx) ([]byte, error) Fetch bytes from the descriptor's presigned URL
polipage.RenderToFile(ctx, client, in, path) error Render and stream to disk

Configuration

All options live in the option subpackage and are passed to NewClient (variadic). Per-call options (currently only WithIdempotencyKey) are passed to individual methods as the final variadic argument.

Option Type Default Description
option.WithAPIKey(key) string (required) API key; it names its environment (pp_{env}_…)
option.WithBaseURL(url) string https://api.poli.page API base URL
option.WithMaxRetries(n) int 2 Retry budget on top of the initial attempt
option.WithRetryDelay(d) time.Duration 500 * time.Millisecond Base exponential-backoff delay
option.WithTimeout(d) time.Duration 60 * time.Second Per-request fallback deadline (when ctx has none)
option.WithHTTPClient(c) *http.Client new client Inject custom transport / TLS / middleware
option.WithLogger(l) *slog.Logger discard One DEBUG/attempt, WARN/retry, ERROR/terminal
option.WithOnRetry(fn) func(polipage.RetryEvent) nil Fires before each retry sleep
option.WithOnError(fn) func(error) nil Fires on terminal failure
option.WithIdempotencyKey(key) string UUID4 Per-call — override the auto-generated key
option.WithRequestTimeout(d) time.Duration WithTimeout value Per-call — override per-request deadline (no-op if ctx has a deadline)
option.WithHeader(key, value) string, string — Construction + per-call — extra request header; per-call wins on duplicate keys

Error handling

The SDK returns a single error type, *polipage.Error, for every failure. Use errors.Is against the package-level sentinels for quick branching, or errors.As to inspect the full value:

import (
    "errors"
    "log"

    polipage "github.com/poli-page/sdk-go"
)

pdf, err := client.Render.PDF(ctx, in)
if err != nil {
    if errors.Is(err, polipage.ErrUnauthorized) { return refreshCredentials() } // MISSING_API_KEY or INVALID_API_KEY

    var pErr *polipage.Error
    if errors.As(err, &pErr) {
        if pErr.IsRateLimitError()  { return queueForLater() } // any 429: quota codes are never retried
        if pErr.IsValidationError() { log.Println("bad input:", pErr.Message) }
        if pErr.IsNetworkError()    { log.Println("network/timeout") }
        log.Printf("code=%s status=%d requestId=%s\n", pErr.Code, pErr.StatusCode, pErr.RequestID)
    }
    return err
}

A sentinel matches by error code, not by HTTP status. ErrRateLimit matches QUOTA_EXCEEDED and OVERAGE_CAP_EXCEEDED; to catch every 429, use IsRateLimitError() as above. ErrNotFound matches only the generic NOT_FOUND code, so check the specific codes of a failed render:

var pErr *polipage.Error
if errors.As(err, &pErr) {
    switch pErr.Code {
    case polipage.ErrCodeProjectNotFound:
        return showBanner("No project matches this slug: check it, or whether the project was renamed or deleted.")
    case polipage.ErrCodeProjectNotInEnvironment:
        return showBanner("The project is not published in this key's environment.")
    case polipage.ErrCodeTemplateNotFound:
        return show404()
    case polipage.ErrCodeOrganizationPaymentGrace:
        return showBanner("Billing is past due.")
    case polipage.ErrCodeOrganizationCancelled:
        return showBanner("Subscription cancelled — service is read-only.")
    case polipage.ErrCodeDocumentGone:
        return show410() // the document was deleted or has expired
    }
}

Unknown codes from a newer API pass through verbatim in pErr.Code.

Cancellation

Pass a context.Context with a deadline or cancel to abort a render in flight:

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

pdf, err := client.Render.PDF(ctx, in)

When the context is canceled, the SDK returns *polipage.Error with Code == ErrCodeAborted, also while the PDF is downloaded or streamed; when the deadline expires, Code == ErrCodeTimeout. Caller-aborted errors are never retried.

Observability

The SDK ships with log/slog integration as the primary observability mechanism:

logger := slog.New(slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelDebug}))
client := polipage.NewClient(
    option.WithAPIKey(os.Getenv("POLI_PAGE_API_KEY")),
    option.WithLogger(logger),
)

One DEBUG line per HTTP attempt, one WARN per retry, one ERROR per terminal failure. Headers and bodies that could contain the API key are never logged.

For SDK-level events that don't fit a log line, register hooks:

client := polipage.NewClient(
    option.WithAPIKey(os.Getenv("POLI_PAGE_API_KEY")),
    option.WithOnRetry(func(e polipage.RetryEvent) {
        metrics.Counter("poli.retry").With("attempt", e.Attempt).Inc()
    }),
    option.WithOnError(func(err error) {
        sentry.CaptureException(err)
    }),
)

Hooks are synchronous, optional, and panic-safe — a hook that panics never breaks the request. For request/response inspection (full headers + body), inject a custom *http.Client via option.WithHTTPClient and add a middleware transport there — see the next section.

Middleware via http.RoundTripper

The SDK's hooks fire at SDK-level events (retry, terminal error). For full-fidelity HTTP middleware — tracing, request signing, response caching, fixture recording, request/response logging with bodies — wrap a http.RoundTripper and pass it via option.WithHTTPClient. This is the stdlib idiom; the SDK does nothing special to enable it.

type tracingTransport struct {
    base http.RoundTripper
}

func (t *tracingTransport) RoundTrip(req *http.Request) (*http.Response, error) {
    start := time.Now()
    resp, err := t.base.RoundTrip(req)
    log.Printf("%s %s → %d in %s", req.Method, req.URL.Path, resp.StatusCode, time.Since(start))
    return resp, err
}

client := polipage.NewClient(
    option.WithAPIKey(os.Getenv("POLI_PAGE_API_KEY")),
    option.WithHTTPClient(&http.Client{
        Transport: &tracingTransport{base: http.DefaultTransport},
    }),
)

Compose multiple layers by chaining RoundTrippers (each wraps the previous one's base). The SDK's retry loop runs outside this transport, so each RoundTrip corresponds to one attempt — log every retry by counting RoundTrip calls per request.

Per-call tracing IDs

For one-off per-request headers (a tracing ID, a tenant override, a feature flag), use option.WithHeader as a per-call option without rebuilding the client:

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

pdf, err := client.Render.PDF(ctx, in,
    option.WithHeader("X-Trace-Id", traceID),
    option.WithRequestTimeout(5*time.Second), // overrides client default for this call only
)

Per-call header keys win over construction-time WithHeader entries for the same key; both override the SDK's own headers (Authorization, Content-Type, etc.) if you supply matching keys — caller's responsibility.

Retries & idempotency

The SDK retries on 5xx, 429, network errors, and timeouts, except QUOTA_EXCEEDED, OVERAGE_CAP_EXCEEDED (429) and STORAGE_ACCESS_DENIED (502), which waiting cannot fix: those are returned at once. Before retry n (1-based) the SDK waits RetryDelay × 2^(n−1) × j, with j a random factor in [0.5, 1.5); a Retry-After header (integer seconds or HTTP-date, clamped to 30 s) replaces it when the server sends one. Retry-After-Ms is ignored. Go's HTTP transport may resend a GET once on a stale keep-alive connection; it never resends a POST. Over HTTP/2, a POST the server refuses before processing it (refused stream, GOAWAY) comes back as network_error and goes through the SDK's own retry; with WithMaxRetries(0) it is returned to you. Every POST sends an auto-generated Idempotency-Key (UUID v4), the same on every retry of that call; the API does not deduplicate on it, so a retried render can store a second document. Pass option.WithIdempotencyKey("…") as the last argument to override:

_, err := client.Render.PDF(ctx, in, option.WithIdempotencyKey("inv-INV-001"))

Type system

  • RenderInput is a struct and the only input of Render.PDF, PDFStream, Document and RenderToFile. Project, Template and Data are required; Format, Orientation, Locale and Metadata are optional and left out of the request when unset. Pass a non-nil Data (an empty map[string]any{} is sent as {}).
  • PageFormat, Orientation and ThumbnailFormat are string types with named constants (PageFormatA4, OrientationLandscape, ThumbnailFormatWebP, …).
  • Nullable wire fields of DocumentDescriptor (ProjectID, TemplateSlug, Orientation, Locale, …) are *string to distinguish JSON null from empty strings. Environment is a plain string: any environment name the API returns.

Concurrency & thread-safety

*polipage.Client is safe for concurrent use. Create one client per process and reuse it across goroutines — the underlying *http.Client pools connections automatically. The SDK has no async/sync split; goroutines + context.Context cover both concurrency and cancellation.

var wg sync.WaitGroup
results := make([]*polipage.DocumentDescriptor, len(invoices))
errs    := make([]error, len(invoices))
for i, inv := range invoices {
    wg.Add(1)
    go func(i int, inv Invoice) {
        defer wg.Done()
        results[i], errs[i] = client.Render.Document(ctx, polipage.RenderInput{
            Project:  "billing",
            Template: "invoice",
            Data:     inv.Data(),
        })
    }(i, inv)
}
wg.Wait()

Runtime support

Server-side only. The SDK runs on:

  • Go 1.25+ — current and N-1 stable Go releases.

Browsers / WebAssembly are not supported. API keys are secrets, whatever their environment, and must never be shipped to a browser. Call the SDK from your backend (HTTP server, CLI, worker, lambda) and proxy the result to the client.

Requirements

Go 1.25 or later. The SDK has zero runtime dependencies — only the Go standard library.

Documentation & support

License

MIT © Poli Page

Documentation

Overview

Package polipage is the official Go SDK for Poli Page.

Construct a client with NewClient, then call methods on the Render or Documents namespaces. The client is safe for concurrent use — share one instance per process so the underlying *http.Client pools connections automatically.

Index

Examples

Constants

View Source
const (
	// ErrCodeInvalidOptions marks constructor or per-call option validation
	// failures (empty API key, malformed metadata, etc.).
	ErrCodeInvalidOptions = "invalid_options"
	// ErrCodeNetworkError marks DNS / connection / TLS failures.
	ErrCodeNetworkError = "network_error"
	// ErrCodeTimeout marks a per-request deadline being exceeded.
	ErrCodeTimeout = "timeout"
	// ErrCodeAborted marks caller-initiated cancellation (a canceled
	// context.Context). Never retryable.
	ErrCodeAborted = "aborted"
	// ErrCodeUnknownError is the fallback code when an error-body JSON has
	// no recognised fields.
	ErrCodeUnknownError = "unknown_error"
	// ErrCodeDownloadFailed marks a failure fetching a presigned PDF URL.
	ErrCodeDownloadFailed = "DOWNLOAD_FAILED"
	// ErrCodeInternalError is the SDK-side fallback when the response body
	// is unparseable or missing. Distinct from ErrCodeAPIInternalError, which
	// has the same wire value but originates from the API.
	ErrCodeInternalError = "INTERNAL_ERROR"
	// ErrCodeIOFailed marks local filesystem failures from [RenderToFile]
	// (MkdirAll / Create / write errors). The underlying os error is wrapped
	// in Cause — use errors.Unwrap or errors.As(*os.PathError) to inspect.
	ErrCodeIOFailed = "io_failed"
)

SDK-internal error codes. These do not appear in API responses; the SDK sets them on errors raised before, around, or after the HTTP call.

View Source
const (
	ErrCodeMissingAPIKey              = "MISSING_API_KEY" //nolint:gosec // G101: error-code identifier, not a credential
	ErrCodeInvalidAPIKey              = "INVALID_API_KEY" //nolint:gosec // G101: error-code identifier, not a credential
	ErrCodeForbidden                  = "FORBIDDEN"
	ErrCodeOrganizationCancelled      = "ORGANIZATION_CANCELLED"
	ErrCodeOrganizationPurged         = "ORGANIZATION_PURGED"
	ErrCodeOrganizationPaymentGrace   = "ORGANIZATION_PAYMENT_GRACE"
	ErrCodeOrganizationMigrating      = "ORGANIZATION_MIGRATING"
	ErrCodeEnvironmentMismatch        = "ENVIRONMENT_MISMATCH"
	ErrCodeEnvironmentProtected       = "ENVIRONMENT_PROTECTED"
	ErrCodeNotFound                   = "NOT_FOUND"
	ErrCodeProjectNotFound            = "PROJECT_NOT_FOUND"
	ErrCodeProjectNotInEnvironment    = "PROJECT_NOT_IN_ENVIRONMENT"
	ErrCodeTemplateNotFound           = "TEMPLATE_NOT_FOUND"
	ErrCodeDocumentNotFound           = "DOCUMENT_NOT_FOUND"
	ErrCodeDocumentGone               = "DOCUMENT_GONE"
	ErrCodeValidationError            = "VALIDATION_ERROR"
	ErrCodeMissingData                = "MISSING_DATA"
	ErrCodeMissingTemplateSlug        = "MISSING_TEMPLATE_SLUG"
	ErrCodeProjectRequiredForDocument = "PROJECT_REQUIRED_FOR_DOCUMENT"
	ErrCodeTemplateError              = "TEMPLATE_ERROR"
	ErrCodeTemplateUnsafe             = "TEMPLATE_UNSAFE"
	ErrCodeEmptyDocument              = "EMPTY_DOCUMENT"
	ErrCodeRenderBodyTooLarge         = "RENDER_BODY_TOO_LARGE"
	ErrCodeDocumentTooLarge           = "DOCUMENT_TOO_LARGE"
	ErrCodeThumbnailsNotAvailable     = "THUMBNAILS_NOT_AVAILABLE"
	ErrCodeQuotaExceeded              = "QUOTA_EXCEEDED"
	ErrCodeOverageCapExceeded         = "OVERAGE_CAP_EXCEEDED"
	ErrCodeRateLimitExceeded          = "RATE_LIMIT_EXCEEDED"
	// ErrCodePublishInProgress is a 503 answered while a publish locks the
	// project in the key's environment. Retried like any 5xx, honouring
	// Retry-After.
	ErrCodePublishInProgress   = "PUBLISH_IN_PROGRESS"
	ErrCodeStorageAccessDenied = "STORAGE_ACCESS_DENIED"
	ErrCodeRenderTimeout       = "RENDER_TIMEOUT"
	// ErrCodeAPIInternalError shares the wire value "INTERNAL_ERROR" with
	// ErrCodeInternalError; kept as a separate constant for call-site clarity.
	ErrCodeAPIInternalError = "INTERNAL_ERROR"
)

Known API error codes pass through verbatim from the deployed API. Callers may see codes not in this list — the SDK preserves whatever the API returns.

View Source
const (
	// DefaultBaseURL is the production Poli Page API base URL. Override via
	// option.WithBaseURL to target another API host.
	DefaultBaseURL = "https://api.poli.page"

	// DefaultMaxRetries is the default retry budget: 2 retries on top of the
	// initial attempt, so 3 attempts total before a retryable error surfaces.
	DefaultMaxRetries = 2

	// DefaultRetryDelay is the base exponential-backoff delay. The first
	// retry waits ~DefaultRetryDelay × jitter, the second ~2×, etc.
	DefaultRetryDelay = 500 * time.Millisecond

	// DefaultTimeout is the per-request fallback deadline applied when the
	// caller's context has no deadline of its own.
	DefaultTimeout = 60 * time.Second
)

User-facing default values for client construction. Internal SDK values (header names, jitter bounds, path templates) live in internal/constants instead, per sdk-go-plan.md §17.

Variables

View Source
var (
	ErrUnauthorized          = &Error{Code: ErrCodeMissingAPIKey}         // also matches INVALID_API_KEY
	ErrForbidden             = &Error{Code: ErrCodeForbidden}             //
	ErrNotFound              = &Error{Code: ErrCodeNotFound}              //
	ErrDocumentNotFound      = &Error{Code: ErrCodeDocumentNotFound}      //
	ErrDocumentGone          = &Error{Code: ErrCodeDocumentGone}          // 410 — document deleted or expired
	ErrValidation            = &Error{Code: ErrCodeValidationError}       //
	ErrRateLimit             = &Error{Code: ErrCodeQuotaExceeded}         // also matches OVERAGE_CAP_EXCEEDED
	ErrTimeout               = &Error{Code: ErrCodeTimeout}               //
	ErrAborted               = &Error{Code: ErrCodeAborted}               //
	ErrNetwork               = &Error{Code: ErrCodeNetworkError}          //
	ErrDownloadFailed        = &Error{Code: ErrCodeDownloadFailed}        //
	ErrIOFailed              = &Error{Code: ErrCodeIOFailed}              // local filesystem write failure from RenderToFile
	ErrOrganizationCancelled = &Error{Code: ErrCodeOrganizationCancelled} // 403 — subscription cancelled, service is read-only
	ErrOrganizationPurged    = &Error{Code: ErrCodeOrganizationPurged}    // 410 — organization data has been purged
)

Sentinel errors. Use with errors.Is for ergonomic call-site checks:

if errors.Is(err, polipage.ErrNotFound) {
    return nil // treat 404 as absent without further inspection
}

Each sentinel is a zero-status *Error keyed by Code; (*Error).Is matches receivers whose Code equals the sentinel's, with two logical groups (auth, rate-limit) that compare equal across related codes.

Functions

func RenderToFile

func RenderToFile(ctx context.Context, c *Client, in RenderInput, path string) error

RenderToFile renders a PDF and streams the bytes straight to disk. Parent directories are created if missing; an existing file at path is overwritten. Memory-bounded — streaming via io.Copy regardless of PDF size.

Errors from the render call propagate as-is (typed *Error). I/O errors surface as *Error with Code == ErrCodeInvalidOptions and the underlying os error wrapped in Cause — use errors.Unwrap to inspect.

Method order matches the spec: callers should reuse a single *Client across calls. RenderToFile is a free function because it is a thin convenience over Render.PDFStream; keeping it off *Client matches the platform spec §2.

Example

ExampleRenderToFile streams a PDF directly to disk. Parent directories are created if missing; existing files are overwritten. Memory stays bounded regardless of document size.

package main

import (
	"context"
	"log"
	"os"

	polipage "github.com/poli-page/sdk-go"
	"github.com/poli-page/sdk-go/option"
)

func main() {
	client := polipage.NewClient(option.WithAPIKey(os.Getenv("POLI_PAGE_API_KEY")))
	err := polipage.RenderToFile(
		context.Background(),
		client,
		polipage.RenderInput{
			Project: "billing", Template: "invoice",
			Data: map[string]any{"invoiceNumber": "INV-001"},
		},
		"out/invoice.pdf",
	)
	if err != nil {
		log.Fatal(err)
	}
}

Types

type Client

type Client struct {

	// Render is the render namespace. See [Render] for available methods.
	Render *Render

	// Documents is the stored-documents namespace. See [Documents] for
	// available methods.
	Documents *Documents
	// contains filtered or unexported fields
}

Client is the Poli Page SDK entry point. Construct one via NewClient and reuse it for the lifetime of the process.

func NewClient

func NewClient(opts ...option.RequestOption) *Client

NewClient constructs a Poli Page SDK client. Options are applied in order; later options override earlier ones for the same field.

Validation is deferred: NewClient never returns an error. If a required option is missing (e.g. option.WithAPIKey), the first method call returns an *Error with Code == ErrCodeInvalidOptions.

client := polipage.NewClient(
    option.WithAPIKey(os.Getenv("POLI_PAGE_API_KEY")),
)
Example

ExampleNewClient demonstrates the canonical construction of a Client. The API key is the only required option; sensible defaults (3 attempts total, 500ms base retry delay, 60s per-request timeout) apply to everything else.

package main

import (
	"os"

	polipage "github.com/poli-page/sdk-go"
	"github.com/poli-page/sdk-go/option"
)

func main() {
	client := polipage.NewClient(
		option.WithAPIKey(os.Getenv("POLI_PAGE_API_KEY")),
		option.WithMaxRetries(3),
	)
	// Share one client across the process — *http.Client pools
	// connections automatically.
	_ = client
}
Example (WithLogger)

ExampleNewClient_withLogger wires a structured slog logger. The SDK emits one DEBUG line per HTTP attempt, one WARN per retry, and one ERROR per terminal failure. Headers and bodies that could contain the API key are not logged.

package main

import (
	"log/slog"
	"os"

	polipage "github.com/poli-page/sdk-go"
	"github.com/poli-page/sdk-go/option"
)

func main() {
	logger := slog.New(slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelDebug}))
	client := polipage.NewClient(
		option.WithAPIKey(os.Getenv("POLI_PAGE_API_KEY")),
		option.WithLogger(logger),
	)
	_ = client
}
Example (WithTimeout)

ExampleNewClient_withTimeout shows the recommended cancellation / deadline pattern: a per-call context.WithTimeout. The Client's own WithTimeout is a fallback applied only when the caller's ctx has no deadline.

package main

import (
	"context"
	"os"
	"time"

	polipage "github.com/poli-page/sdk-go"
	"github.com/poli-page/sdk-go/option"
)

func main() {
	client := polipage.NewClient(option.WithAPIKey(os.Getenv("POLI_PAGE_API_KEY")))

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	_, _ = client.Render.PDF(ctx, polipage.RenderInput{
		Project: "billing", Template: "invoice",
		Data: map[string]any{},
	})
}

type DocumentDescriptor

type DocumentDescriptor struct {
	DocumentID     string `json:"documentId"`
	OrganizationID string `json:"organizationId"`

	ProjectID    *string `json:"projectId"`
	ProjectSlug  *string `json:"projectSlug"`
	TemplateID   *string `json:"templateId"`
	TemplateSlug *string `json:"templateSlug"`
	APIKeyID     *string `json:"apiKeyId"`

	// Environment is the name of the environment the document was rendered
	// in ("sandbox", "live", or any other environment of the organization),
	// returned as the API sent it.
	Environment string     `json:"environment"`
	Format      PageFormat `json:"format"`

	Orientation *string `json:"orientation"`
	Locale      *string `json:"locale"`

	PageCount int   `json:"pageCount"`
	SizeBytes int64 `json:"sizeBytes"`

	CreatedAt string `json:"createdAt"`
	ExpiresAt string `json:"expiresAt"`

	// Metadata is the caller-supplied metadata echoed by the server. Always
	// non-nil after a successful decode — a server null is normalised to an
	// empty map so callers can range without a nil check.
	Metadata RenderMetadata `json:"metadata"`

	// PresignedPDFURL has a ~15-minute TTL. Re-fetch via Documents.Get when
	// expired.
	PresignedPDFURL string `json:"presignedPdfUrl"`
	// contains filtered or unexported fields
}

DocumentDescriptor is a stored document returned by Render.Document and Documents.Get. Top-level fields are system-controlled; Metadata echoes caller-supplied data.

Nullable wire fields (ProjectID, ProjectSlug, TemplateID, TemplateSlug, APIKeyID, Orientation, Locale) are *string so the SDK can distinguish JSON null from an empty string.

func (*DocumentDescriptor) DownloadPDF

func (d *DocumentDescriptor) DownloadPDF(ctx context.Context, opts ...option.DownloadOption) ([]byte, error)

DownloadPDF fetches the PDF bytes from PresignedPDFURL. The URL has a ~15-minute TTL — when it expires, call Documents.Get(id) to refresh and retry.

A non-2xx answer returns *Error with Code == ErrCodeDownloadFailed and the storage status; a network failure, before the answer or while the body is read, returns ErrCodeDownloadFailed without a status. Canceling ctx returns ErrCodeAborted. The underlying error is wrapped in Cause.

The request is unauthenticated and is not subject to the SDK's retry policy: presigned-URL fetches go directly to S3 (or equivalent).

Example

ExampleDocumentDescriptor_DownloadPDF fetches the bytes from a descriptor's PresignedPDFURL. Useful when you persisted the descriptor earlier and want to defer the byte fetch until needed.

package main

import (
	"context"
	"log"
	"os"

	polipage "github.com/poli-page/sdk-go"
	"github.com/poli-page/sdk-go/option"
)

func main() {
	client := polipage.NewClient(option.WithAPIKey(os.Getenv("POLI_PAGE_API_KEY")))
	doc, err := client.Documents.Get(context.Background(), "doc_abc123")
	if err != nil {
		log.Fatal(err)
	}
	pdf, err := doc.DownloadPDF(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	_ = os.WriteFile("invoice.pdf", pdf, 0o644)
}

type DocumentPreviewResult

type DocumentPreviewResult struct {
	HTML      string
	PageCount int
}

DocumentPreviewResult is the result of Documents.Preview — the paginated HTML for an already-stored document and the page count the engine emitted at render time.

type Documents

type Documents struct {
	// contains filtered or unexported fields
}

Documents is the stored-documents namespace exposed as client.Documents. It hosts the four lifecycle methods defined in spec §6.

func (*Documents) Delete

func (d *Documents) Delete(ctx context.Context, id string, opts ...option.RequestOption) error

Delete soft-deletes a stored document. The PDF is purged from storage; metadata is retained for audit. Deleting an already-deleted document also succeeds: the API answers 204 either way.

Spec §6.4.

Example

ExampleDocuments_Delete soft-deletes a stored document. Deleting it again also succeeds; subsequent reads of the same documentId surface as polipage.ErrDocumentGone.

package main

import (
	"context"
	"log"
	"os"

	polipage "github.com/poli-page/sdk-go"
	"github.com/poli-page/sdk-go/option"
)

func main() {
	client := polipage.NewClient(option.WithAPIKey(os.Getenv("POLI_PAGE_API_KEY")))
	if err := client.Documents.Delete(context.Background(), "doc_abc123"); err != nil {
		log.Fatal(err)
	}
}

func (*Documents) Get

Get retrieves a stored document's descriptor with a fresh presigned URL. The returned descriptor's DocumentDescriptor.DownloadPDF uses the parent client for transport; calling it after the URL's ~15-minute TTL returns *Error{Code: ErrCodeDownloadFailed} — re-fetch via Get.

Spec §6.1.

Example

ExampleDocuments_Get retrieves a stored document with a fresh presigned URL. The PDF download URL has a ~15-minute TTL — call Get again to reissue it once expired.

package main

import (
	"context"
	"fmt"
	"log"
	"os"

	polipage "github.com/poli-page/sdk-go"
	"github.com/poli-page/sdk-go/option"
)

func main() {
	client := polipage.NewClient(option.WithAPIKey(os.Getenv("POLI_PAGE_API_KEY")))
	doc, err := client.Documents.Get(context.Background(), "doc_abc123")
	if err != nil {
		log.Fatal(err)
	}
	pdf, err := doc.DownloadPDF(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%d bytes\n", len(pdf))
}

func (*Documents) Preview

Preview retrieves a stored document's paginated HTML. The engine does no work — this is a cheap read.

The deployed API returns the HTML as text/html (not a JSON envelope) and the page count via the X-Document-Page-Count response header. Missing or unparseable headers are tolerated: PageCount defaults to 0.

Spec §6.2.

Example

ExampleDocuments_Preview retrieves the stored HTML for a document. The engine performs no work on this call — it's a cheap read suitable for preview UIs and snapshot tests in CI.

package main

import (
	"context"
	"fmt"
	"log"
	"os"

	polipage "github.com/poli-page/sdk-go"
	"github.com/poli-page/sdk-go/option"
)

func main() {
	client := polipage.NewClient(option.WithAPIKey(os.Getenv("POLI_PAGE_API_KEY")))
	res, err := client.Documents.Preview(context.Background(), "doc_abc123")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%d page(s), %d chars HTML\n", res.PageCount, len(res.HTML))
}

func (*Documents) Thumbnails

func (d *Documents) Thumbnails(ctx context.Context, id string, options ThumbnailOptions, opts ...option.RequestOption) ([]Thumbnail, error)

Thumbnails generates page thumbnails for a stored document. Every option is optional: pass a zero ThumbnailOptions for the API defaults (400 px wide JPEG, every page).

The deployed API expects the options wrapped under a top-level "thumbnails" key on the wire and returns the array under the same key — both are handled here so callers see a plain slice. With no option set the body is {}.

Spec §6.3.

Example

ExampleDocuments_Thumbnails generates page thumbnails. Every option is optional: a zero polipage.ThumbnailOptions gets the API defaults (400 px wide JPEG, every page). Tier-gated on the API — Free tier returns THUMBNAILS_NOT_AVAILABLE.

package main

import (
	"context"
	"fmt"
	"log"
	"os"

	polipage "github.com/poli-page/sdk-go"
	"github.com/poli-page/sdk-go/option"
)

func main() {
	client := polipage.NewClient(option.WithAPIKey(os.Getenv("POLI_PAGE_API_KEY")))
	thumbs, err := client.Documents.Thumbnails(
		context.Background(),
		"doc_abc123",
		polipage.ThumbnailOptions{
			Width:  840,
			Format: polipage.ThumbnailFormatWebP,
			Pages:  []int{1, 2, 3},
		},
	)
	if err != nil {
		log.Fatal(err)
	}
	for _, t := range thumbs {
		fmt.Printf("page %d: %dx%d %s\n", t.Page, t.Width, t.Height, t.ContentType)
	}
}

type Error

type Error struct {
	// Code is the machine-readable error code. For HTTP errors this is the
	// API's code field (verbatim); for SDK-internal errors it is one of
	// ErrCode* (invalid_options, network_error, timeout, aborted, etc.).
	Code string
	// StatusCode is the HTTP response status. Zero for SDK-internal errors
	// (network failure, timeout, abort, constructor validation).
	StatusCode int
	// Message is the human-readable error message.
	Message string
	// RequestID is the x-request-id header value when present; empty otherwise.
	RequestID string
	// Cause is the underlying error this *Error wraps. nil when the error
	// originated in the SDK itself with no upstream cause.
	Cause error
}

Error is the single error type raised by the SDK: API errors, network failures, timeouts, caller cancellation, and constructor validation failures all surface as *Error.

Callers can use either errors.Is against the package-level sentinels (ErrNotFound, ErrUnauthorized, etc.) for quick branching or errors.As to inspect Code, StatusCode, RequestID, and the wrapped Cause.

Example (TypedInspection)

ExampleError_typedInspection demonstrates the two idiomatic ways to branch on errors returned by the SDK: errors.Is against the package-level sentinels for quick checks, and errors.As to inspect the full *Error value (Code, StatusCode, RequestID, Cause).

package main

import (
	"context"
	"errors"
	"fmt"

	polipage "github.com/poli-page/sdk-go"
	"github.com/poli-page/sdk-go/option"
)

func main() {
	client := polipage.NewClient(option.WithAPIKey("pp_sandbox_bad_key"))
	_, err := client.Render.PDF(context.Background(), polipage.RenderInput{
		Project: "x", Template: "y", Data: map[string]any{},
	})
	if errors.Is(err, polipage.ErrUnauthorized) {
		// refresh credentials, surface to the user, ...
	}
	var pErr *polipage.Error
	if errors.As(err, &pErr) {
		fmt.Printf("code=%s status=%d requestID=%s\n", pErr.Code, pErr.StatusCode, pErr.RequestID)
	}
}

func (*Error) Error

func (e *Error) Error() string

Error implements the error interface. The format is stable but not parseable — callers wanting structured access should use errors.As to extract *Error.

func (*Error) Is

func (e *Error) Is(target error) bool

Is supports errors.Is by matching the receiver against package-level sentinel errors via their Code field. A small number of logical groups (auth: MISSING_API_KEY / INVALID_API_KEY; rate-limit: QUOTA_EXCEEDED / OVERAGE_CAP_EXCEEDED) compare equal so the corresponding sentinel matches either underlying code.

func (*Error) IsAuthError

func (e *Error) IsAuthError() bool

IsAuthError reports whether the response was 401 or 403 — invalid, missing, or unauthorized API key.

func (*Error) IsNetworkError

func (e *Error) IsNetworkError() bool

IsNetworkError reports whether the error is a transport-level failure (DNS, connection refused, TLS) or a per-request timeout. Caller-aborted requests (ErrCodeAborted) and constructor-validation failures (ErrCodeInvalidOptions) do not count.

func (*Error) IsRateLimitError

func (e *Error) IsRateLimitError() bool

IsRateLimitError reports whether the response was 429 — too many requests.

func (*Error) IsRetryable

func (e *Error) IsRetryable() bool

IsRetryable reports whether the SDK considers this error retryable: a network failure, a timeout, a 5xx or a 429, except QUOTA_EXCEEDED, OVERAGE_CAP_EXCEEDED and STORAGE_ACCESS_DENIED, which last until the monthly reset or until the bucket access is restored. A DOWNLOAD_FAILED is never retryable, whatever the storage status. Caller-aborted requests are never retryable. The SDK already retries internally up to the configured limit; this predicate is useful when an outer queue or scheduler decides whether to re-enqueue the job.

func (*Error) IsValidationError

func (e *Error) IsValidationError() bool

IsValidationError reports whether the response was 400 — request payload failed validation.

func (*Error) ToPayload

func (e *Error) ToPayload() Payload

ToPayload returns the canonical wire payload for framework integrations. The Error.StatusCode field stays 0 for transport failures — only the payload surfaces 503/504, so callers reading StatusCode are not affected.

func (*Error) Unwrap

func (e *Error) Unwrap() error

Unwrap exposes the wrapped Cause to errors.Is and errors.As.

type Orientation

type Orientation string

Orientation is the page orientation: portrait or landscape.

const (
	OrientationPortrait  Orientation = "portrait"
	OrientationLandscape Orientation = "landscape"
)

Canonical orientations.

type PageFormat

type PageFormat string

PageFormat is a canonical Poli Page page format. Values match the strings accepted by the deployed API and shared across every SDK.

const (
	PageFormatA3        PageFormat = "A3"
	PageFormatA4        PageFormat = "A4"
	PageFormatA5        PageFormat = "A5"
	PageFormatA6        PageFormat = "A6"
	PageFormatB4        PageFormat = "B4"
	PageFormatB5        PageFormat = "B5"
	PageFormatLetter    PageFormat = "Letter"
	PageFormatLegal     PageFormat = "Legal"
	PageFormatTabloid   PageFormat = "Tabloid"
	PageFormatExecutive PageFormat = "Executive"
	PageFormatStatement PageFormat = "Statement"
	PageFormatFolio     PageFormat = "Folio"
)

Canonical page formats. See the platform spec for the authoritative list.

type Payload

type Payload struct {
	Code      string `json:"code"`
	Message   string `json:"message"`
	Status    int    `json:"status"`
	RequestID string `json:"requestId"`
}

Payload is the canonical wire shape for framework integrations: {code, message, status, requestId}. Status is the API HTTP status for status-bearing failures, 503 for network failures, 504 for timeouts, and 0 otherwise. In JSON, a zero Status and an empty RequestID are null.

Do not embed Payload in another struct: its MarshalJSON is promoted, so the embedding struct marshals as the Payload alone and its own fields drop out of the JSON. Use a named field instead.

func (Payload) MarshalJSON added in v0.11.0

func (p Payload) MarshalJSON() ([]byte, error)

MarshalJSON writes Status and RequestID as null when they are unknown (zero and empty), as the SDK specification's toPayload() does.

type Render

type Render struct {
	// contains filtered or unexported fields
}

Render is the render namespace exposed as client.Render: PDF, PDFStream and Document, the spec §5 surface.

func (*Render) Document

func (r *Render) Document(ctx context.Context, in RenderInput, opts ...option.RequestOption) (*DocumentDescriptor, error)

Document renders a PDF, stores it server-side, and returns the descriptor. The PDF bytes are NOT fetched — call (*DocumentDescriptor).DownloadPDF when you need them, or use Render.PDF / Render.PDFStream for the one-call shortcut.

The API renders the project's copy published in the API key's environment. Variadic options apply per-call; the most useful one is option.WithIdempotencyKey when the caller has a natural identifier (e.g. an invoice number) and wants to override the auto-generated UUID4.

Example

ExampleRender_Document stores the document server-side and returns its descriptor without auto-fetching the PDF bytes. Use when you want to persist the documentId for later access (preview, thumbnails, re-download) without paying for the bytes up front.

package main

import (
	"context"
	"fmt"
	"log"
	"os"

	polipage "github.com/poli-page/sdk-go"
	"github.com/poli-page/sdk-go/option"
)

func main() {
	client := polipage.NewClient(option.WithAPIKey(os.Getenv("POLI_PAGE_API_KEY")))
	doc, err := client.Render.Document(context.Background(), polipage.RenderInput{
		Project: "billing", Template: "invoice",
		Data: map[string]any{"invoiceNumber": "INV-001"},
		// Metadata is echoed back on Documents.Get — primitives only.
		Metadata: polipage.RenderMetadata{"customerId": "cust_123"},
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("documentId=%s pages=%d\n", doc.DocumentID, doc.PageCount)
}

func (*Render) PDF

func (r *Render) PDF(ctx context.Context, in RenderInput, opts ...option.RequestOption) ([]byte, error)

PDF renders a PDF and returns the raw bytes. Two HTTP calls under the hood: POST /v1/render to produce a stored document, then GET PresignedPDFURL to fetch the bytes.

Example

ExampleRender_PDF renders a template of a project into a []byte, using the project's copy published in the API key's environment. Two HTTP calls under the hood; one from the caller's point of view.

package main

import (
	"context"
	"fmt"
	"log"
	"os"

	polipage "github.com/poli-page/sdk-go"
	"github.com/poli-page/sdk-go/option"
)

func main() {
	client := polipage.NewClient(option.WithAPIKey(os.Getenv("POLI_PAGE_API_KEY")))
	pdf, err := client.Render.PDF(context.Background(), polipage.RenderInput{
		Project:  "billing",
		Template: "invoice",
		Data:     map[string]any{"invoiceNumber": "INV-001"},
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("PDF size: %d bytes\n", len(pdf))
}
Example (WithIdempotencyKey)

ExampleRender_PDF_withIdempotencyKey supplies a natural identifier as the Idempotency-Key so retried requests against the same logical invoice never produce duplicate documents server-side.

package main

import (
	"context"
	"os"

	polipage "github.com/poli-page/sdk-go"
	"github.com/poli-page/sdk-go/option"
)

func main() {
	client := polipage.NewClient(option.WithAPIKey(os.Getenv("POLI_PAGE_API_KEY")))
	_, _ = client.Render.PDF(
		context.Background(),
		polipage.RenderInput{
			Project: "billing", Template: "invoice",
			Data: map[string]any{"invoiceNumber": "INV-001"},
		},
		option.WithIdempotencyKey("inv-INV-001"),
	)
}

func (*Render) PDFStream

func (r *Render) PDFStream(ctx context.Context, in RenderInput, opts ...option.RequestOption) (io.ReadCloser, error)

PDFStream is like Render.PDF but returns the PDF as an io.ReadCloser so the caller can stream it directly to an HTTP response, an S3 upload, or a file without buffering.

The caller MUST close the returned ReadCloser when done, even on early return paths.

Example

ExampleRender_PDFStream streams PDF bytes directly to a destination without buffering — memory stays bounded regardless of document size. The caller MUST close the returned ReadCloser.

package main

import (
	"context"
	"io"
	"log"
	"os"

	polipage "github.com/poli-page/sdk-go"
	"github.com/poli-page/sdk-go/option"
)

func main() {
	client := polipage.NewClient(option.WithAPIKey(os.Getenv("POLI_PAGE_API_KEY")))
	body, err := client.Render.PDFStream(context.Background(), polipage.RenderInput{
		Project: "billing", Template: "invoice",
		Data: map[string]any{"invoiceNumber": "INV-001"},
	})
	if err != nil {
		log.Fatal(err)
	}
	defer func() { _ = body.Close() }()

	out, _ := os.Create("invoice.pdf")
	defer func() { _ = out.Close() }()
	_, _ = io.Copy(out, body)
}

type RenderInput

type RenderInput struct {
	// Required.
	Project  string         `json:"project"`
	Template string         `json:"template"`
	Data     map[string]any `json:"data"`

	// Optional — omitted from the wire when unset.
	Format      PageFormat     `json:"format,omitempty"`
	Orientation Orientation    `json:"orientation,omitempty"`
	Locale      string         `json:"locale,omitempty"`
	Metadata    RenderMetadata `json:"metadata,omitempty"`
}

RenderInput is the input of every render method: a template of a project, rendered with the project's copy published in the API key's environment. Only these seven fields are sent; unset optional fields are omitted from the wire.

type RenderMetadata

type RenderMetadata = map[string]any

RenderMetadata is free-form caller metadata forwarded to the API and echoed on responses that support it. Values must be primitives (string, number, bool); nested maps and slices are rejected at serialise time with *Error{Code: ErrCodeInvalidOptions}.

type RequestEvent

type RequestEvent = clientconfig.RequestEvent

RequestEvent is delivered to the OnRequest hook before each HTTP attempt. Aliased to clientconfig.RequestEvent so option.WithOnRequest and the re-export here both spell the type identically.

type ResponseEvent

type ResponseEvent = clientconfig.ResponseEvent

ResponseEvent is delivered to the OnResponse hook once per 2xx attempt, after its body has been read in full; never for a 2xx whose body read fails. DurationMs runs from just before the request is sent until the body is read. Aliased to clientconfig.ResponseEvent.

type RetryEvent

type RetryEvent = clientconfig.RetryEvent

RetryEvent is delivered to the OnRetry hook before each retry sleep. Aliased to clientconfig.RetryEvent so option.WithOnRetry and the re-export here can both spell the type identically.

type Thumbnail

type Thumbnail struct {
	Page        int    `json:"page"`
	Width       int    `json:"width"`
	Height      int    `json:"height"`
	ContentType string `json:"contentType"`
	Data        string `json:"data"`
}

Thumbnail is a single page thumbnail returned by Documents.Thumbnails. Data is base64-encoded image bytes; the caller decodes via encoding/base64.StdEncoding.DecodeString as needed.

type ThumbnailFormat

type ThumbnailFormat string

ThumbnailFormat is the image format for a generated thumbnail. The API default is JPEG, which also accepts a Quality option.

const (
	ThumbnailFormatJPEG ThumbnailFormat = "jpeg"
	ThumbnailFormatWebP ThumbnailFormat = "webp"
	ThumbnailFormatPNG  ThumbnailFormat = "png"
)

Canonical thumbnail formats.

type ThumbnailOptions

type ThumbnailOptions struct {
	// Width is the thumbnail width in pixels, 50-2000. API default: 400.
	Width int `json:"width,omitempty"`
	// Format selects jpeg, webp or png. API default: jpeg.
	Format ThumbnailFormat `json:"format,omitempty"`
	// Quality is the JPEG quality 1-100. Only valid when Format is JPEG.
	Quality int `json:"quality,omitempty"`
	// Pages selects specific 1-based page numbers. Empty means all pages.
	Pages []int `json:"pages,omitempty"`
}

ThumbnailOptions configures a thumbnail-generation request. Every field is optional: a zero value is not sent and the API applies its default. The SDK does not check bounds; the API answers 400 VALIDATION_ERROR.

Directories

Path Synopsis
cmd
demo command
Demo of the official Poli Page SDK for Go.
Demo of the official Poli Page SDK for Go.
examples
documents-delete command
Demonstrates: client.Documents.Delete(ctx, id) — soft-delete a stored document.
Demonstrates: client.Documents.Delete(ctx, id) — soft-delete a stored document.
documents-get command
Demonstrates: client.Documents.Get(ctx, id) — fetch a stored document.
Demonstrates: client.Documents.Get(ctx, id) — fetch a stored document.
documents-preview command
Demonstrates: client.Documents.Preview(ctx, id) — get a stored document's HTML preview.
Demonstrates: client.Documents.Preview(ctx, id) — get a stored document's HTML preview.
documents-thumbnails command
Demonstrates: client.Documents.Thumbnails(ctx, id, options) — page thumbnails for a stored document.
Demonstrates: client.Documents.Thumbnails(ctx, id, options) — page thumbnails for a stored document.
poli-page-constructor command
Demonstrates: polipage.NewClient(options) — the only entry point.
Demonstrates: polipage.NewClient(options) — the only entry point.
render-document command
Demonstrates: client.Render.Document(ctx, in) — render and store a PDF server-side.
Demonstrates: client.Render.Document(ctx, in) — render and store a PDF server-side.
render-pdf command
Demonstrates: client.Render.PDF(ctx, in) — renders the project's copy published in the key's environment.
Demonstrates: client.Render.PDF(ctx, in) — renders the project's copy published in the key's environment.
render-pdf-stream command
Demonstrates: client.Render.PDFStream(ctx, in) — renders the project's copy published in the key's environment.
Demonstrates: client.Render.PDFStream(ctx, in) — renders the project's copy published in the key's environment.
render-to-file command
Demonstrates: polipage.RenderToFile(ctx, client, in, path) — streams a PDF to disk.
Demonstrates: polipage.RenderToFile(ctx, client, in, path) — streams a PDF to disk.
internal
clientconfig
Package clientconfig holds the mutable Config used by the polipage client and the option subpackage.
Package clientconfig holds the mutable Config used by the polipage client and the option subpackage.
constants
Package constants holds the SDK-internal string values shared between the transport layer and the namespace implementations: API path templates, canonical HTTP header names, and any other magic strings that would otherwise be re-typed at multiple call sites.
Package constants holds the SDK-internal string values shared between the transport layer and the namespace implementations: API path templates, canonical HTTP header names, and any other magic strings that would otherwise be re-typed at multiple call sites.
http
Package http contains the pure transport-layer helpers used by the top-level polipage client.
Package http contains the pure transport-layer helpers used by the top-level polipage client.
uuid
Package uuid generates RFC 4122 §4.4 version-4 UUIDs from crypto/rand.
Package uuid generates RFC 4122 §4.4 version-4 UUIDs from crypto/rand.
version
Package version exposes the SDK build version, embedded into the User-Agent header by the transport layer.
Package version exposes the SDK build version, embedded into the User-Agent header by the transport layer.
Package option holds the functional options for constructing a polipage.Client and overriding behavior on individual method calls.
Package option holds the functional options for constructing a polipage.Client and overriding behavior on individual method calls.
scripts
extract-api command
Command extract-api walks the public Poli Page Go SDK surface with go/packages + go/doc and emits the docs/src/content/docs/reference/ tree consumed by the Starlight site.
Command extract-api walks the public Poli Page Go SDK surface with go/packages + go/doc and emits the docs/src/content/docs/reference/ tree consumed by the Starlight site.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL