Downloads API Reference¶
The Downloads API provides secure file download endpoints for digital products. Customers use these endpoints to access purchased digital files using HMAC-signed download tokens.
Base URL: /api/merchello/downloads
Authentication: All endpoints require Umbraco member authentication ([Authorize]). Customers can only access their own downloads.
How Digital Downloads Work¶
When a customer purchases a digital product, Merchello generates secure download links with HMAC-signed tokens. Each link has:
- An expiry date (configurable per product via
DownloadLinkExpiryDays) - A maximum download count (configurable per product via
MaxDownloadsPerLink) - A customer ownership check (the token is bound to the customer who purchased the product)
The download flow:
- Customer visits their account or order confirmation page
- Frontend calls
GET /customerorGET /invoice/{invoiceId}to get download links - Customer clicks a download link, which calls
GET /{token} - Merchello validates the token, checks expiry/limits, streams the file, and records the download
Endpoints¶
GET /{token}¶
Download a file using a secure token.
Path parameters:
| Parameter | Type | Description |
|---|---|---|
token |
string | The HMAC-signed download token |
Rate limiting: This endpoint uses the downloads rate limiting policy to prevent abuse.
Security checks performed:
- Customer must be authenticated
- Token is validated via
ValidateDownloadTokenAsync(HMAC signature, expiry, customer ownership) - Download count is checked against the maximum
- File path is validated to prevent path traversal attacks (must stay within
wwwroot)
Response (200): The file is streamed with the appropriate content type and filename.
Supported content types:
| Extension | Content Type |
|---|---|
.pdf |
application/pdf |
.zip |
application/zip |
.mp3 |
audio/mpeg |
.mp4 |
video/mp4 |
.jpg, .jpeg |
image/jpeg |
.png |
image/png |
.gif |
image/gif |
.doc |
application/msword |
.docx |
application/vnd.openxmlformats-officedocument.wordprocessingml.document |
.xls |
application/vnd.ms-excel |
.xlsx |
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet |
.epub |
application/epub+zip |
.mobi |
application/x-mobipocket-ebook |
| Other | application/octet-stream |
Error responses:
| Status | Reason |
|---|---|
401 |
Customer not authenticated |
403 |
Download limit reached |
404 |
Invalid/expired token, or file not found on disk |
400 |
Other validation failure |
GET /customer¶
Get all download links for the current authenticated customer.
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
includeExpired |
bool | false |
Include expired download links |
Response (200):
[
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"fileName": "ebook-guide.pdf",
"downloadUrl": "/api/merchello/downloads/abc123token...",
"expiresUtc": "2026-04-28T00:00:00Z",
"productName": "",
"maxDownloads": 5,
"downloadCount": 2,
"remainingDownloads": 3,
"lastDownloadUtc": "2026-03-28T14:30:00Z",
"isExpired": false,
"isDownloadLimitReached": false
}
]
Error responses:
| Status | Reason |
|---|---|
401 |
Customer not authenticated |
404 |
Customer not found |
Tip: Use
includeExpired=trueto show customers their full download history, even for links that have expired. You can then display a "Contact support" message next to expired downloads.
GET /invoice/{invoiceId}¶
Get all download links for a specific invoice. This is typically shown on the order confirmation page or order detail page.
Path parameters:
| Parameter | Type | Description |
|---|---|---|
invoiceId |
Guid | The invoice ID |
Response (200): Same format as the customer downloads endpoint.
Security: The endpoint verifies that the authenticated customer owns the invoice. If the invoice belongs to a different customer, a 403 Forbidden is returned.
Error responses:
| Status | Reason |
|---|---|
401 |
Customer not authenticated |
403 |
Invoice belongs to a different customer |
404 |
Customer or invoice not found |
Frontend Integration Example¶
Here is a simple example of how you might display downloads on a customer account page:
// Fetch customer downloads
const response = await fetch('/api/merchello/downloads/customer', {
credentials: 'include' // important: send auth cookies
});
const downloads = await response.json();
downloads.forEach(download => {
if (download.isExpired) {
// Show "Link expired" message
} else if (download.isDownloadLimitReached) {
// Show "Download limit reached" message
} else {
// Show download button linking to download.downloadUrl
// Display: "Downloaded 2 of 5 times"
}
});
Warning: Digital products require a customer account -- guest checkout is not supported for digital products. Make sure your checkout flow requires sign-in or account creation when the basket contains digital items.