How delivery works
Delivery has two halves: turning a paid order into an entitlement, and turning a click on a link into a file. They are separate on purpose, because the first happens once and the second happens every time.
From a paid order to a download
When Shopify reports an order as paid, the app looks at each line item, finds the files attached to that product or variant, and creates one download for each match.
Each download records who it belongs to, which file it covers, and the limits and delivery rule that applied at the moment it was created. The same order and the same file can only ever produce one download, which matters because Shopify retries webhooks and a retry must not issue a second one or send a second email.
The entitlement is always created at payment, whatever your delivery rule says. A held order has its downloads waiting; only the email waits with them. That is what makes a buyer’s entitlement survive a policy change, a webhook that arrives late, or a merchant who never fulfils anything.
When the email goes out
Under Settings, then File delivery, the Delivery card sets the store-wide rule. Three choices:
- When the order is paid. The usual one, and the default. You can add a wait before sending, using Wait before sending and Unit.
- When the order is fulfilled. The files are held until you fulfil the order. This is the preorder answer, for when the money arrives before the file is finished.
- When I release it by hand. Nothing is sent automatically. Every order waits for Deliver now on the Orders page.
An individual product assignment can override the store rule, so one preorder product can hold its files while everything else goes out at payment. Links already issued keep the rule they were issued under.
Choosing when downloads go out is part of the paid plans. On the free plan, downloads go out as soon as the order is paid, and assignments that already carry their own rule keep following it.
Which email carries the link
The same settings page chooses between two ways of reaching the buyer, and both at once is a fine answer:
- Separate email after the order. One delivery email per paid order, listing every file in it. Its wording lives on the Emails page.
- Download link added to the order confirmation email. No separate email from the app. Buyers use a button inside Shopify’s own order confirmation, which you design and paste in yourself.
This setting only affects automatic delivery. Deliver now and Resend on the Orders page always send when you ask.
Marking the order fulfilled
Mark digital line items fulfilled after delivery does what it says, and is on for new installs. A digital line item nobody will ever pack should not sit in your fulfilment queue. It can be turned off store-wide or on a single assignment.
From a click to a file
The link in the buyer’s email points at the app, never at storage. It opens a page listing the files on that order, and nothing has touched storage yet.
Every click on a download is authorised again from scratch before anything is handed over. The app checks that the download has not been revoked, has not expired, and that the file is still there and fully uploaded; it checks the click against the total cap and against the rolling window; it records the attempt; and only then does it hand the browser a storage address that is valid for about a minute. The browser fetches the bytes straight from storage.
Why it is built this way
A valid link is not a permanent permission. Revocation, expiry and limits live on the record, so they take effect on the very next click rather than whenever some cached copy expires. There is no already-issued address out in the world to worry about.
The app is never in the path of the bytes. Files go from your browser to storage on upload, and from storage to the buyer on download. That is why a multi-gigabyte file is no slower to serve than a small one, and why downloads are not metered.
Storage addresses are short lived. Long enough for any real client to start a transfer, short enough that a leaked one is worthless before it can circulate. Every retry goes back through the full set of checks.