Learn how to create a downloadable product using the REST API and the Adobe Commerce Admin.
You must specify which domains are permitted to allow downloads. Domains are added to the project’s env.php
file via the command line. The env.php
file details the domains allowed to contain downloadable content. An error occurs if a downloadable product is created using the REST API before the php.env
file is updated:
{
"message": "Link URL's domain is not in list of downloadable_domains in env.php."
}
To set the domain, connect to the server: bin/magento downloadable:domains:add www.example.com
Once that is complete, the env.php
is modified inside the downloadable_domains array.
'downloadable_domains' => [
'www.example.com'
],
Now that the domain is added to the env.php
, you can create a downloadable product in the Adobe Commerce Admin or by using the REST API.
See Configuration Reference to learn more. See [CLI reference for Adobe Commerce](https://experienceleague.adobe.com/docs/commerce-operations/reference/magento-open-source.html#downloadable%3Adomains%3Aadd for more details.
On some versions of Adobe Commerce, you might get the following error when a product is edited in the Adobe Commerce Admin. The product is created using the REST API but the linked download has a null
price.
Deprecated Functionality: number_format(): Passing null to parameter #1 ($num) of type float is deprecated in /app/vendor/magento/module-downloadable/Ui/DataProvider/Product/Form/Modifier/Data/Links.php on line 228
.
To fix this error, use the update link API: POST V1/products/{sku}/downloadable-links.
See the Update a product download link using cURL section for more info.
This example shows how to create a downloadable product using cURL when the file to download is not on the same server. This use case is typical if the file is stored in an S3 bucket or other digital asset manager.
curl --location '{{your.url.here}}/rest/default/V1/products' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{Your Bearer Token}}' \
--header 'Cookie: PHPSESSID=b78cae2338f12d846d233d4e9486607e; private_content_version=564dde2976849891583a9a649073f01e' \
--data '{
"product": {
"sku": "POSTMAN-download-product-1",
"name": "POSTMAN download product-1",
"attribute_set_id": 4,
"price": 9.9,
"status": 1,
"visibility": 4,
"type_id": "downloadable",
"extension_attributes": {
"website_ids": [
1
],
"downloadable_product_links": [
{
"title": "My url link",
"sort_order": 1,
"is_shareable": 1,
"price": 0,
"number_of_downloads": 0,
"link_type": "url",
"link_url": "{{location.url.where.file.exists}}/some-file.zip",
"sample_type": "url",
"sample_url": "{{location.url.where.file.exists}}/sample-file.zip"
}
],
"downloadable_product_samples": []
},
"product_links": [],
"options": [],
"media_gallery_entries": [],
"tier_prices": []
}
}
'
This example demonstrates how to use cURL to create a downloadable product from the Adobe Commerce Admin when the file is stored on the same server as the Adobe Commerce application.
In this use case, when the administrator managing the catalog chooses upload file
, the file is transferred to the pub/media/downloadable/files/links/
directory. Automation creates and moves the files to their respective locations based on the following pattern:
link_file
section of the path uses the portion of the path appended to the pub/media/downloadable/files/links/
directory.For example, if the uploaded file is named download-example.zip
:
The file is uploaded to the path pub/media/downloadable/files/links/d/o/
.
The subdirectories /d
and /d/o
are created if they do not exist.
The final path to the file is /pub/media/downloadable/files/links/d/o/download-example.zip
.
The link_url
value for this example is d/o/download-example.zip
curl --location '{{your.url.here}}/rest/default/V1/products' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{Your Bearer Token}}' \
--header 'Cookie: PHPSESSID=571f5ebe48857569cd56bde5d65f83a2; private_content_version=9f3286b427812be6aec0b04cae33ba35' \
--data '{
"product": {
"sku": "sample-local-download-file",
"name": "A downloadable product with locally hosted download file",
"attribute_set_id": 4,
"price": 33,
"status": 1,
"visibility": 4,
"type_id": "downloadable",
"extension_attributes": {
"website_ids": [
1
],
"downloadable_product_links": [
{
"title": "an api version of already upload file",
"sort_order": 1,
"is_shareable": 1,
"price": 0,
"number_of_downloads": 0,
"link_type": "file",
"link_file": "/d/o/downloadable-file-demo-file-upload.zip",
"sample_type": null
}
],
"downloadable_product_samples": []
},
"product_links": [],
"options": [],
"media_gallery_entries": [],
"tier_prices": []
}
}'
curl --location '{{your.url.here}}/rest/default/V1/products/POSTMAN-download-product-1' \
--header 'Authorization: Bearer {{Your Bearer Token}}' \
--header 'Cookie: PHPSESSID=b78cae2338f12d846d233d4e9486607e; private_content_version=564dde2976849891583a9a649073f01e'
Use the endpoint rest/all/V1/products/{sku}/downloadable-links
The SKU
is the product ID that was generated when the product was created. For example in the code sample below, it is the number 39, but make sure it is updated to use the ID from your website. This updates the links for the downloadable products.
{
"link": {
"id": 39,
"title": "My swagger update",
"sort_order": 0,
"is_shareable": 0,
"price": 0,
"number_of_downloads": 0,
"link_type": "url",
"link_file": "{{your.url.here}}/some-file.zip",
"link_url": "{{your.url.here}}/some-file.zip",
"link_file_content": {
"file_data": "{{your.url.here}}/some-file.zip",
"extension_attributes": {}
}
},
"isGlobalScopeContent": true
}
When you update a product download link using cURL, the URL includes the SKU for the product that is being updated. In the following code example, the SKU is abcd12345
. When you submit the command, change value to match the SKU for the product you want to update.
curl --location '{{your.url.here}}/rest/all/V1/products/abcd12345/downloadable-links' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{Your Bearer Token}}' \
--header 'Cookie: PHPSESSID=fa5d76f4568982adf369f758e8cb9544; private_content_version=564dde2976849891583a9a649073f01e' \
--data '{
"link": {
"id": {{The ID of the download link for example 39}},
"title": "My update",
"sort_order": 0,
"is_shareable": 0,
"price": 0,
"number_of_downloads": 0,
"link_type": "url",
"link_file": "{{your.url.here}}/some-file.zip",
"link_url": "{{your.url.here}}/some-file.zip",
"link_file_content": {
"file_data": "{{your.url.here}}/some-file.zip",
"extension_attributes": {}
}
},
"isGlobalScopeContent": true
}'