/** * Retry policy for transient Prodigi API failures. * * Every field is optional; unset fields fall back to {@link DEFAULT_RETRY_POLICY}. */ interface RetryOptions { /** Total attempts including the first, clamped to at least 1. Default `3`. */ maxAttempts?: number; /** Base delay for the first retry, in milliseconds. Default `500`. */ initialDelayMs?: number; /** Upper bound on the computed exponential delay, in milliseconds. Default `8000`. */ maxDelayMs?: number; /** * Longest `Retry-After` value that will be waited out, in milliseconds. * A longer `Retry-After` aborts the retry and throws. Default `30000`. */ maxRetryAfterMs?: number; /** Apply equal jitter to computed delays. Default `true`. */ jitter?: boolean; /** Retry when `fetch` itself rejects (DNS failure, socket reset, ...). Default `true`. */ retryNetworkErrors?: boolean; } /** A fully resolved {@link RetryOptions} with no unset fields. */ type RetryPolicy = Required; /** Defaults applied when a client is constructed without a `retry` option. */ declare const DEFAULT_RETRY_POLICY: RetryPolicy; interface HttpClientOptions { baseUrl: string; apiKey?: string; /** Retry policy for transient failures, or `false` to disable retries. */ retry?: RetryOptions | false; } interface RequestOptions { /** * Marks a non-idempotent method as safe to replay, e.g. because the payload * carries an idempotency key. Ignored for `GET`, which is always retryable. */ idempotent?: boolean; } /** Low-level HTTP client for making authenticated requests to the Prodigi API. */ declare class HttpClient { readonly baseUrl: string; readonly retryPolicy: RetryPolicy; private readonly apiKey; constructor(options: HttpClientOptions); /** * Send a GET request. Retried automatically on transient failures. * @param path - API endpoint path (e.g. "/orders"). * @param query - Optional query parameters appended to the URL. */ get(path: string, query?: Record): Promise; /** * Send a POST request. * * Not retried by default — a replayed POST can duplicate a side effect. Pass * `{ idempotent: true }` only when the payload makes replay safe. * * @param path - API endpoint path (e.g. "/orders"). * @param body - Optional JSON-serializable request body. * @param options - Per-request overrides. */ post(path: string, body?: unknown, options?: RequestOptions): Promise; private buildUrl; private request; /** * Delay before replaying a retryable error response, or `null` to give up. * * A `Retry-After` header wins over the computed backoff, but only up to * `maxRetryAfterMs` — beyond that the caller is better served by an error than * by a silent multi-minute stall. */ private retryDelayFor; } type ShippingMethod = "Budget" | "Standard" | "StandardPlus" | "Express" | "Overnight"; type Sizing = "fillPrintArea" | "fitPrintArea" | "stretchToPrintArea"; type OrderStage = "InProgress" | "Complete" | "Cancelled"; type OrderStatus = "AwaitingPayment" | "NotYetDownloaded" | "Downloaded" | "Printing" | "QualityControl" | "Dispatched" | "WithCarrier" | "InTransit" | "Delivered" | "Cancelled" | "OnHold"; interface Cost { amount: string; currency: string; } interface Address { line1: string; line2?: string; postalOrZipCode: string; countryCode: string; townOrCity: string; stateOrCounty?: string; } interface Recipient { name: string; email?: string; phoneNumber?: string; address: Address; } interface Asset { id?: string; printArea?: string; url: string; status?: string; md5Hash?: string; thumbnailUrl?: string; pageCount?: number; } interface OrderItemAttribute { name: string; value: string; } interface BrandingAsset { url: string; } interface Branding { postcard?: BrandingAsset; flyer?: BrandingAsset; packing_slip_bw?: BrandingAsset; packing_slip_color?: BrandingAsset; sticker_exterior_round?: BrandingAsset; sticker_exterior_rectangle?: BrandingAsset; sticker_interior_round?: BrandingAsset; sticker_interior_rectangle?: BrandingAsset; } interface PackingSlip { url: string; status?: string; } interface CreateOrderItem { merchantReference?: string; sku: string; copies: number; sizing: Sizing; attributes?: Record; assets: Asset[]; recipientCost?: Cost; } interface CreateOrderRequest { merchantReference?: string; shippingMethod: ShippingMethod; recipient: Recipient; items: CreateOrderItem[]; metadata?: Record; idempotencyKey?: string; callbackUrl?: string; branding?: Branding; packingSlip?: { url: string; }; } interface StatusChange { status: string; timestamp: string; } interface FulfillmentLocation { countryCode: string; labCode: string; } interface OrderItem { id: string; status: string; merchantReference?: string; sku: string; copies: number; sizing: Sizing; attributes: Record; assets: Asset[]; recipientCost?: Cost; statusChanges?: StatusChange[]; } interface Shipment { id: string; carrier: { name: string; service: string; }; tracking?: { url: string; number: string; }; status: string; dispatchDate?: string; items: { itemId: string; }[]; fulfillmentLocation: FulfillmentLocation; } interface ChargeItem { id?: string; shipmentId?: string; itemId?: string; cost?: Cost; } interface Charge { id: string; prodigiInvoiceNumber?: string; totalCost: Cost; chargeType: string; items: ChargeItem[]; } interface AuthorisationDetails { authorisationUrl: string; paymentDetails?: Cost; } interface Issue { objectId: string; errorCode: string; description: string; authorisationDetails?: AuthorisationDetails; } type StatusDetailStatus = "NotStarted" | "InProgress" | "Complete" | "Error"; interface OrderStatusDetail { stage: OrderStage; issues: Issue[]; details: { downloadAssets: StatusDetailStatus; printReadyAssetsPrepared: StatusDetailStatus; allocateProductionLocation: StatusDetailStatus; inProduction: StatusDetailStatus; shipping: StatusDetailStatus; }; } interface Order { id: string; created: string; lastUpdated: string; callbackUrl?: string; merchantReference?: string; shippingMethod: ShippingMethod; idempotencyKey?: string; status: OrderStatusDetail; charges: Charge[]; shipments: Shipment[]; recipient: Recipient; items: OrderItem[]; metadata?: Record; branding?: Branding; packingSlip?: PackingSlip; } interface OrderOutcome { outcome: "Ok" | "Created" | "AlreadyExists" | "CreatedWithIssues" | "OnHold"; order: Order; traceParent: string; } /** * Status values accepted by the `status` filter on `GET /orders`. * * Deliberately distinct from {@link OrderStage}: the filter is camelCase and * adds `draft` and `awaitingPayment`, which are not stage values. */ type OrderListStatus = "draft" | "awaitingPayment" | "inProgress" | "complete" | "cancelled"; interface ListOrdersParams { /** Number of orders to return. Must be 1–100. Server default is 10. */ top?: number; /** Number of records to skip before the next `top` orders. Must be >= 0. Server default is 0. */ skip?: number; /** Limit to orders placed after this UTC date/time. A `Date` is serialized to ISO 8601. */ createdFrom?: string | Date; /** Limit to orders placed before this UTC date/time. A `Date` is serialized to ISO 8601. */ createdTo?: string | Date; /** Limit to a particular status. */ status?: OrderListStatus; /** Limit to a list of Prodigi order IDs. */ orderIds?: string[]; /** Limit to a range of your own order references. */ merchantReferences?: string[]; } interface ListOrdersResponse { outcome: string; orders: Order[]; /** True when more orders match the filter than were returned in this page. */ hasMore: boolean; /** Absolute URL of the next page, present only when `hasMore` is true. */ nextUrl?: string; traceParent: string; } interface ActionAvailability { isAvailable: string; } interface OrderActions { outcome: string; cancel: ActionAvailability; changeRecipientDetails: ActionAvailability; changeShippingMethod: ActionAvailability; changeMetaData: ActionAvailability; traceParent: string; } interface UpdateShippingMethodRequest { shippingMethod: ShippingMethod; } interface UpdateRecipientRequest { recipient: Recipient; } interface UpdateMetadataRequest { metadata: Record; } interface ShipmentUpdateResult { shipmentId: string; successful: boolean; errorMessage?: string; } interface ActionOutcome { outcome: string; order: Order; traceParent: string; } interface ShippingActionOutcome extends ActionOutcome { shipmentUpdateResults?: ShipmentUpdateResult[]; } interface RecipientActionOutcome extends ActionOutcome { shippingUpdateResults?: ShipmentUpdateResult[]; } interface QuoteItem { sku: string; copies: number; attributes?: Record; assets: { printArea: string; }[]; } interface CreateQuoteRequest { shippingMethod?: ShippingMethod; destinationCountryCode: string; items: QuoteItem[]; currencyCode?: string; } interface QuoteCostItem { id?: string; sku: string; copies: number; unitCost: Cost; totalCost?: Cost; attributes?: Record; assets?: { printArea?: string; }[]; } interface QuoteShipment { carrier: { name: string; service: string; }; cost: Cost; items: string[]; fulfillmentLocation: { countryCode: string; labCode: string; }; } interface Quote { shipmentMethod: string; costSummary: { items: Cost; shipping: Cost; }; items: QuoteCostItem[]; shipments: QuoteShipment[]; } interface QuoteIssue { errorCode: string; description: string; } interface QuoteOutcome { outcome: string; quotes: Quote[]; issues?: QuoteIssue[]; traceParent: string; } interface PrintAreaSize { horizontalResolution: number; verticalResolution: number; } interface Variant { attributes: Record; shipsTo: string[]; printAreaSizes: Record; } interface Product { sku: string; description: string; productDimensions?: { width: number; height: number; units: string; }; attributes: Record; printAreas: Record; variants: Variant[]; } interface ProductOutcome { outcome: string; product: Product; traceParent: string; } interface SpineRequest { sku: string; destinationCountryCode: string; state?: string; numberOfPages: number; } interface SpineResponse { success: boolean; message: string; spineInfo: { widthMm: number; }; } /** Payload of a Prodigi callback: the order at the moment the event was created. */ interface CallbackEventData { order: Order; } /** * Raw CloudEvents v1.0 envelope as delivered by Prodigi. * * @see https://www.prodigi.com/print-api/docs/reference/#callbacks */ interface CallbackEvent { /** CloudEvents spec version, e.g. `"1.0"`. */ specversion: string; /** Reverse-DNS event type with a `#fragment` new value, e.g. `"com.prodigi.order.status.stage.changed#InProgress"`. */ type: string; /** URI of the environment that produced the event, e.g. `"https://api.prodigi.com/v4.0/Orders/"`. */ source: string; /** Unique event id, prefixed `evt_`. */ id: string; /** RFC 3339 timestamp of when the event was generated. */ time: string; /** Always `"application/json"`. */ datacontenttype: string; /** The object that produced the callback — the order id. */ subject: string; data: CallbackEventData; } /** * CloudEvents `type` Prodigi sends when an order changes fulfilment stage. * * Documented verbatim in the Prodigi reference. */ declare const ORDER_STAGE_CHANGED_TYPE = "com.prodigi.order.status.stage.changed"; /** * CloudEvents `type` prefix for shipment callbacks. * * Prodigi documents that callbacks fire "when a shipment is made" and that the * `type` is built from the path to the nested object, but does not publish the * literal string. This prefix follows that documented convention; anything that * does not match still parses, as an {@link UnknownCallbackEvent}. */ declare const ORDER_SHIPMENT_TYPE_PREFIX = "com.prodigi.order.shipments"; /** Fields shared by every parsed callback event. */ interface CallbackEventBase { /** The raw envelope exactly as delivered, for logging or replay. */ event: CallbackEvent; /** Unique event id, prefixed `evt_`. */ id: string; /** Order id the callback relates to (CloudEvents `subject`). */ subject: string; /** RFC 3339 timestamp of when the event was generated. */ time: string; /** The order carried by the callback. */ order: Order; /** CloudEvents `type` with the `#fragment` stripped. */ eventType: string; /** Value after `#` in the CloudEvents `type`, e.g. `"InProgress"`. Empty when absent. */ changedTo: string; } /** * An order moved between fulfilment stages. * * `changedTo` carries the raw fragment; `order.status.stage` carries the same * value already narrowed to {@link OrderStage}. */ interface OrderStageChangedEvent extends CallbackEventBase { kind: "order.status.stage.changed"; } /** A shipment was made against the order. */ interface OrderShipmentEvent extends CallbackEventBase { kind: "order.shipments.shipment"; } /** * A structurally valid callback whose `type` this SDK does not recognise. * * Newer Prodigi event types land here rather than throwing, so a handler keeps * working when Prodigi adds events. Inspect `eventType` and `event` directly. */ interface UnknownCallbackEvent extends CallbackEventBase { kind: "unknown"; } /** Discriminated union of parsed Prodigi callbacks, keyed on `kind`. */ type ProdigiCallbackEvent = OrderStageChangedEvent | OrderShipmentEvent | UnknownCallbackEvent; interface CataloguePricing { source: string; value: string; } interface CatalogueProductSummary { name: string; slug: string; productSlug: string; global: boolean; sizes: string[]; pricing: CataloguePricing[]; manufacturingRegions: string[]; image: string; imageRequired: boolean; loreSlug?: string; } interface CatalogueCategory { name: string; slug: string; fullSlug?: string; images?: string[]; products: Record; subCategories: Record; } /** Response from `GET /api/catalogue` — a map of top-level category slugs to categories. */ type CatalogueListResponse = Record; interface CatalogueVariantAsset { name: string; required: boolean; horizontalInches: number; verticalInches: number; sizeUnits: string; outputDpi: number; fileOutputFormat: string; } interface CatalogueVariantAttribute { value: string[]; } interface CatalogueVariantRow { sku: string; description: string; attributeDescription: string; productType: string; price: string; assets: CatalogueVariantAsset[]; size?: string; orientation?: string; fulfilledFrom?: string; } interface CatalogueVariantColumn { enableSorting: boolean; name: string; filterType: string; options?: string[]; } interface CatalogueVariants { columns: Record; rows: CatalogueVariantRow[]; } interface CatalogueManufacturing { regions: string[]; time: string; shipsTo: string[]; } interface CatalogueProductDetail { name: string; availability: string; description: string[]; features: string[]; manufacturing: CatalogueManufacturing; pricing: CataloguePricing[]; variants: CatalogueVariants; sizes: string[]; } /** Resource for browsing the public Prodigi product catalogue (no authentication required). */ declare class CatalogueResource { private readonly http; constructor(http: HttpClient); /** * List all categories and products in the catalogue. * @returns The full category tree. */ list(): Promise; /** * Get detailed product information including SKU variants, sizes, and pricing. * @param slug - The product slug (e.g. "cold-press-watercolour-paper"). */ get(slug: string): Promise; } /** Resource for managing Prodigi print orders. */ declare class OrdersResource { private readonly http; constructor(http: HttpClient); /** * Submit a new order. * * Retried on transient failures only when `request.idempotencyKey` is set — * without one, replaying the POST could create a duplicate order. * * @param request - Order creation payload. * @returns The created order outcome. */ create(request: CreateOrderRequest): Promise; /** * Fetch a single order by ID. * @param orderId - The Prodigi order ID. */ get(orderId: string): Promise; /** * List orders with optional filtering and pagination. * * Paginate with `top`/`skip`; `hasMore` on the response indicates whether * further pages exist. * * @param params - Optional filters. Omitted filters are not sent. */ list(params?: ListOrdersParams): Promise; /** * Get available actions for an order. * @param orderId - The Prodigi order ID. */ getActions(orderId: string): Promise; /** * Cancel an order. * @param orderId - The Prodigi order ID. */ cancel(orderId: string): Promise; /** * Update the shipping method for an order. * @param orderId - The Prodigi order ID. * @param request - New shipping method details. */ updateShippingMethod(orderId: string, request: UpdateShippingMethodRequest): Promise; /** * Update the recipient for an order. * @param orderId - The Prodigi order ID. * @param request - New recipient details. */ updateRecipient(orderId: string, request: UpdateRecipientRequest): Promise; /** * Update metadata on an order. * @param orderId - The Prodigi order ID. * @param request - New metadata key-value pairs. */ updateMetadata(orderId: string, request: UpdateMetadataRequest): Promise; } /** Resource for requesting shipping and cost quotes. */ declare class QuotesResource { private readonly http; constructor(http: HttpClient); /** * Request a quote for a potential order. * @param request - Quote request payload. * @returns The quote outcome with cost and shipping estimates. */ create(request: CreateQuoteRequest): Promise; } /** Resource for browsing the Prodigi product catalogue. */ declare class ProductsResource { private readonly http; constructor(http: HttpClient); /** * Get product details by SKU. * @param sku - The product SKU. */ get(sku: string): Promise; /** * Calculate spine width for book-type products. * @param request - Spine calculation parameters. */ getSpine(request: SpineRequest): Promise; } type Environment = "sandbox" | "production"; interface ProdigiClientOptions { apiKey: string; environment?: Environment; /** * Retry policy for transient failures (429, 5xx, network errors), or `false` * to disable retries. Defaults to 3 attempts with exponential backoff. */ retry?: RetryOptions | false; } /** * Main client for interacting with the Prodigi API. * * @example * ```ts * const client = new ProdigiClient({ * apiKey: "your-api-key", * environment: "sandbox", * }); * * const order = await client.orders.create(request); * ``` */ declare class ProdigiClient { readonly orders: OrdersResource; readonly quotes: QuotesResource; readonly products: ProductsResource; readonly catalogue: CatalogueResource; readonly environment: Environment; private readonly apiKey; /** * @param options - Client configuration including API key and optional environment. */ constructor(options: ProdigiClientOptions); } /** * Fluent builder for constructing order creation requests. * * @example * ```ts * const request = new OrderBuilder() * .shippingMethod("Standard") * .recipient({ name: "Jane", address: { ... } }) * .addPrint("GLOBAL-PHO-4x6", "https://example.com/photo.jpg") * .build(); * ``` */ declare class OrderBuilder { private _shippingMethod?; private _recipient?; private _items; private _merchantReference?; private _metadata?; private _idempotencyKey?; /** Set the shipping method for the order. */ shippingMethod(method: ShippingMethod): this; /** Set the recipient for the order. */ recipient(recipient: Recipient): this; /** Set an optional merchant reference for the order. */ merchantReference(ref: string): this; /** Attach custom metadata key-value pairs to the order. */ metadata(metadata: Record): this; /** Set an idempotency key to prevent duplicate orders. */ idempotencyKey(key: string): this; /** Add a fully-configured order item. */ addItem(item: CreateOrderItem): this; /** * Add a print item with a single image asset. * @param sku - Product SKU. * @param imageUrl - URL of the image to print. * @param options - Optional copies and sizing overrides. */ addPrint(sku: string, imageUrl: string, options?: { copies?: number; sizing?: CreateOrderItem["sizing"]; }): this; /** * Build the order creation request. * @returns A validated order request payload. * @throws {ProdigiError} If shippingMethod, recipient, or items are missing. */ build(): CreateOrderRequest; } /** Base error class for all Prodigi SDK errors. */ declare class ProdigiError extends Error { constructor(message: string); } /** Error thrown when the Prodigi API returns a non-OK HTTP response. */ declare class ProdigiApiError extends ProdigiError { readonly statusCode: number; readonly traceParent: string | null; readonly data: unknown; /** * @param message - Human-readable error message from the API. * @param statusCode - HTTP status code of the response. * @param traceParent - Trace parent header for request tracing, if present. * @param data - Raw response body from the API. */ constructor(message: string, statusCode: number, traceParent: string | null, data: unknown); } /** Error thrown when an incoming callback payload is not a valid Prodigi event. */ declare class ProdigiWebhookError extends ProdigiError { /** The payload that failed validation, as received. */ readonly payload: unknown; /** * @param message - Why the payload was rejected. * @param payload - The rejected payload, for logging. */ constructor(message: string, payload: unknown); } /** * Structural type guard for a Prodigi callback envelope. * * Checks the CloudEvents envelope and that `data.order.id` is present. It does * not deep-validate the order — Prodigi may add fields at any time. * * This is a shape check, **not** an authenticity check. See * {@link parseCallbackEvent} for why. * * @param payload - Parsed JSON body of the callback request. */ declare function isCallbackEvent(payload: unknown): payload is CallbackEvent; /** * Validate an incoming Prodigi callback and parse it into a typed event. * * ## Authenticity * * Prodigi does **not** sign callbacks. There is no signature header, shared * secret, or other authentication mechanism in the v4.0 API, so this function * cannot and does not verify that a request genuinely came from Prodigi — it * validates shape only. Protect your endpoint by treating the callback URL * itself as the secret (a long unguessable path) and, before acting on * anything that matters, re-reading the order with * `client.orders.get(event.subject)`. * * @param payload - The request body, either a JSON string or already-parsed JSON. * @returns A discriminated union member keyed on `kind`. * @throws {ProdigiWebhookError} If the body is not JSON, or not a Prodigi callback envelope. * * @example * ```ts * const event = parseCallbackEvent(await request.text()); * * switch (event.kind) { * case "order.status.stage.changed": * console.log(event.subject, event.order.status.stage); * break; * case "order.shipments.shipment": * console.log(event.order.shipments); * break; * case "unknown": * console.log("unhandled event", event.eventType); * break; * } * ``` */ declare function parseCallbackEvent(payload: unknown): ProdigiCallbackEvent; export { type ActionAvailability, type ActionOutcome, type Address, type Asset, type AuthorisationDetails, type Branding, type BrandingAsset, type CallbackEvent, type CallbackEventBase, type CallbackEventData, type CatalogueCategory, type CatalogueListResponse, type CatalogueManufacturing, type CataloguePricing, type CatalogueProductDetail, type CatalogueProductSummary, type CatalogueVariantAsset, type CatalogueVariantAttribute, type CatalogueVariantColumn, type CatalogueVariantRow, type CatalogueVariants, type Charge, type ChargeItem, type Cost, type CreateOrderItem, type CreateOrderRequest, type CreateQuoteRequest, DEFAULT_RETRY_POLICY, type Environment, type FulfillmentLocation, type Issue, type ListOrdersParams, type ListOrdersResponse, ORDER_SHIPMENT_TYPE_PREFIX, ORDER_STAGE_CHANGED_TYPE, type Order, type OrderActions, OrderBuilder, type OrderItem, type OrderItemAttribute, type OrderListStatus, type OrderOutcome, type OrderShipmentEvent, type OrderStage, type OrderStageChangedEvent, type OrderStatus, type OrderStatusDetail, type PackingSlip, type PrintAreaSize, ProdigiApiError, type ProdigiCallbackEvent, ProdigiClient, type ProdigiClientOptions, ProdigiError, ProdigiWebhookError, type Product, type ProductOutcome, type Quote, type QuoteCostItem, type QuoteIssue, type QuoteItem, type QuoteOutcome, type QuoteShipment, type Recipient, type RecipientActionOutcome, type RetryOptions, type RetryPolicy, type Shipment, type ShipmentUpdateResult, type ShippingActionOutcome, type ShippingMethod, type Sizing, type SpineRequest, type SpineResponse, type StatusChange, type StatusDetailStatus, type UnknownCallbackEvent, type UpdateMetadataRequest, type UpdateRecipientRequest, type UpdateShippingMethodRequest, type Variant, isCallbackEvent, parseCallbackEvent };