Digital Downloads
Deliver purchased digital files (PDF, MP3, e-book) and let customers download them
Overview
A product can carry one or more digital assets (PDF, MP3, e-book, software) that the buyer receives automatically once the order is paid. When an order transitions to paid (an online gateway confirms payment, an admin marks it paid, or a gift card fully covers the total), Behio creates a download grant for every active digital asset of the purchased products. Unpaid orders (cash on delivery, bank transfer before payment) never receive grants.
Grants enforce an optional per-customer download limit (maxDownloads) and expiry (expiresAfterDays, counted from the grant). Files are served through short-lived signed URLs; the limit and expiry are enforced server-side, so a leaked URL cannot be reused past its budget.
List the customer's downloads
Requires customer authentication. Returns every grant across all the customer's orders:
const { data } = await client.customer.getDownloads();
// {
// items: [{
// id: string,
// orderId: string | null,
// fileName: string,
// productName: string | null, // localized, for display
// productSlug: string | null,
// fileSize: number, // bytes
// mimeType: string,
// version: string | null,
// downloadCount: number,
// maxDownloads: number | null, // null = unlimited
// remainingDownloads: number | null, // null = unlimited
// lastDownloadAt: number | null,
// expiresAt: number | null, // epoch ms, null = never
// isExpired: boolean,
// isMaxedOut: boolean,
// createdAt: number,
// }]
// }Get a signed download URL
Mint a short-lived (15 min) signed URL for one grant. This counts against the download budget and is rejected (403) once the grant is expired or maxed out:
const { data, error } = await client.customer.getDownloadUrl(downloadId);
// data: { url, fileName, mimeType, fileSize, remainingDownloads, expiresAt }
if (data) window.open(data.url, "_blank");Check isExpired / isMaxedOut on the grant to disable the download button before calling.
Order detail
Paid order details carry the same grants inline, so you can show a download section on the order / confirmation page without a second call:
const { data: order } = await client.orders.get(orderNumber);
order?.downloads; // DigitalDownload[], empty for unpaid or physical-only ordersProduct badge
The product detail exposes isDigital: boolean, true when the product has at least one active digital asset. Use it to render an "instant delivery" badge on the PDP.
Guest orders
A guest who verified an order via the order-access flow can mint a download URL with the order-access token, scoped to that one order:
const { data } = await client.orders.getAccessDownloadUrl(accessToken, downloadId);Admin
Merchants attach digital assets to a product in the admin, or via MCP (eshop-digital-asset-create, eshop-digital-assets-list, eshop-digital-asset-update, eshop-digital-asset-delete) with fileUrl (an R2/S3 object), fileSize, mimeType, and optional maxDownloads + expiresAfterDays.