> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dodopayments.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Digital Product Delivery

> Deliver downloadable files, external links, and download instructions automatically when customers purchase. Digital Product Delivery is the Digital Files entitlement type. Files are issued with presigned download URLs on every grant and revoked when access is withdrawn.

## Overview

Digital Product Delivery is the **Digital Files** entitlement type. You upload your files once to a Digital Files entitlement, attach the entitlement to a product, and Dodo Payments delivers presigned download links to every paying customer through email and the customer portal.

The entitlement supports:

* **Hosted file uploads**: store files on Dodo Payments and serve them via short-lived presigned URLs.
* **External download links**: link to files hosted on Dropbox, Google Drive, S3, or any URL.
* **Download instructions**: free-form text shown to the customer on their order page and in the delivery email.

You can mix all three on a single entitlement.

## Key Features

<CardGroup cols={2}>
  <Card title="File Upload" icon="upload">
    Upload files (PDF, ZIP, images, videos, etc.) up to 100 MB. Files are streamed and stored efficiently.
  </Card>

  <Card title="Multiple Files" icon="files">
    Attach as many files as you need to a single entitlement.
  </Card>

  <Card title="External Links" icon="link">
    Provide external download links (Dropbox, Google Drive, signed S3 URLs) as an alternative or addition.
  </Card>

  <Card title="Presigned URLs" icon="lock">
    Hosted files are served via short-lived presigned URLs. Each download URL expires automatically after roughly 15 minutes.
  </Card>
</CardGroup>

***

## Set up Digital Product Delivery

<Steps>
  <Step title="Open Entitlements">
    Go to **Entitlements** in your Dodo Payments dashboard and click **+** to create a new entitlement.
  </Step>

  <Step title="Choose Digital Files">
    Select **Digital Product Delivery** as the integration.
  </Step>

  <Step title="Add files, links, and instructions">
    Configure any combination of:

    * **Files**: upload one or more files. Each upload returns a `file_id` that's appended to the entitlement.
    * **External URL**: a publicly reachable HTTPS link delivered alongside hosted files.
    * **Instructions**: free-form text shown to the customer (e.g., "Unzip and run setup.sh").

    <Frame>
      <img src="https://mintcdn.com/dodopayments/do-W-dMDGVB_xzr_/images/entitlements/digital-files/create.png?fit=max&auto=format&n=do-W-dMDGVB_xzr_&q=85&s=33ad03277809d14585d247442c191462" alt="Digital Files entitlement with file upload, external URL, and instructions fields" style={{ maxHeight: '500px', width: 'auto' }} width="1669" height="989" data-path="images/entitlements/digital-files/create.png" />
    </Frame>
  </Step>

  <Step title="Save the entitlement">
    Save. The entitlement is now available to attach to any product.
  </Step>
</Steps>

## Attach to Products

Open a product, expand **Advanced Settings → Entitlements & Credits**, and select your Digital Files entitlement. The entitlement is delivered on every successful purchase or active subscription tied to that product.

<Frame caption="Attaching the Digital Files entitlement to a product alongside other entitlements.">
  <img src="https://mintcdn.com/dodopayments/do-W-dMDGVB_xzr_/images/entitlements/attach-to-product.png?fit=max&auto=format&n=do-W-dMDGVB_xzr_&q=85&s=965ad78262791fa8dbb712b4fdf89538" alt="Product entitlements panel showing Digital Product Delivery selected" style={{ maxHeight: '500px', width: 'auto' }} width="2000" height="1197" data-path="images/entitlements/attach-to-product.png" />
</Frame>

***

## How Delivery Works

Digital Files delivery follows the standard [grant lifecycle](/features/entitlements/introduction#how-grants-work):

| Event                                            | Behavior                                                                                                                                                             |
| ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `payment.succeeded` (one-time)                   | Issue a grant. The grant carries presigned download URLs valid for \~15 minutes; customers can refresh them by reopening the email link or the customer portal page. |
| `subscription.active`                            | Issue a grant. Files remain accessible while the subscription is active.                                                                                             |
| `subscription.renewed`                           | No-op. The same grant continues; new presigned URLs are minted on every fetch.                                                                                       |
| `subscription.on_hold` / `cancelled` / `expired` | Revoke the grant. New presigned URLs are no longer issued.                                                                                                           |
| `subscription.plan_changed`                      | Revoke the old grant; issue a new one for the new plan's entitlement.                                                                                                |
| `refund.succeeded` (one-time)                    | Revoke the grant.                                                                                                                                                    |
| Manual revoke                                    | Revoke with `revocation_reason: manual`.                                                                                                                             |

<Warning>
  Revocation stops Dodo Payments from issuing new download URLs, but it does **not** invalidate copies a customer has already downloaded. Treat hosted file downloads as "delivered once read."
</Warning>

***

## Customer Experience

### Purchase Confirmation

After a successful transaction, the customer receives an email with download links and any instructions you configured.

<Frame>
  <img src="https://mintcdn.com/dodopayments/mOQO5ej_lx0yH9p-/images/digital-product-delivery/2.png?fit=max&auto=format&n=mOQO5ej_lx0yH9p-&q=85&s=54f5247f6d67fe5bb48736682995e23e" alt="Purchase confirmation email showing download links for digital products" style={{ maxHeight: '500px', width: 'auto' }} width="1920" height="1080" data-path="images/digital-product-delivery/2.png" />
</Frame>

### Customer Portal Access

Customers can re-fetch download links anytime from the [Customer Portal](/features/customer-portal). The portal page generates fresh presigned URLs on demand, so the same purchase keeps working even after the email's links have expired.

<Frame>
  <img src="https://mintcdn.com/dodopayments/mOQO5ej_lx0yH9p-/images/digital-product-delivery/3.png?fit=max&auto=format&n=mOQO5ej_lx0yH9p-&q=85&s=490c6755474c1f00a93f7adde7dabb63" alt="Customer portal interface showing available digital products for download" style={{ maxHeight: '500px', width: 'auto' }} width="1920" height="1080" data-path="images/digital-product-delivery/3.png" />
</Frame>

<Check>
  Customers can download files directly from confirmation emails or access them anytime through their portal.
</Check>

***

## Manage Files Programmatically

### Upload a file to an entitlement

<CodeGroup>
  ```typescript TypeScript theme={null} theme={null}
  import DodoPayments from 'dodopayments';
  import fs from 'node:fs';

  const client = new DodoPayments({
    bearerToken: process.env['DODO_PAYMENTS_API_KEY'],
    environment: 'test_mode',
  });

  await client.entitlements.files.upload('ent_files_abc', {
    file: fs.createReadStream('./pro-bundle.zip'),
    filename: 'pro-bundle.zip',
  });
  ```

  ```bash cURL theme={null} theme={null}
  curl -X POST "https://test.dodopayments.com/entitlements/ent_files_abc/files" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -F "file=@./pro-bundle.zip" \
    -F "filename=pro-bundle.zip"
  ```
</CodeGroup>

### List grants and resolve download URLs

```typescript theme={null} theme={null}
const grants = await client.entitlements.grants.list('ent_files_abc', {
  customer_id: 'cus_abc123',
});

for (const grant of grants.items) {
  for (const file of grant.digital_product_delivery.files) {
    console.log(file.filename, file.download_url, `expires in ${file.expires_in}s`);
  }
}
```

### Remove a file from an entitlement

```typescript theme={null} theme={null}
await client.entitlements.files.delete('ent_files_abc', 'df_a4f6c1de');
```

***

## Important Considerations

* **Presigned URLs expire quickly.** Download URLs returned in grant payloads or webhook events are valid for \~15 minutes. Don't store them; re-fetch them when the customer needs to download again.
* **Updating files affects future purchases only.** Replacing or removing a file does not retroactively change downloads already issued. Past customers can still re-fetch the version that was current when their grant was created.
* **Refunds don't invalidate downloaded copies.** A customer who already downloaded a file keeps that copy. For revocable content (license-restricted media, time-limited access), pair Digital Files with [License Keys](/features/license-keys) and validate at runtime.
* **For sensitive content, prefer external URLs with their own auth.** Dodo Payments' presigned URLs are short-lived but unauthenticated within their window; anyone with the URL can download in that window. Externally hosted, account-gated content provides stronger guarantees.

***

## API Management

<CardGroup cols={2}>
  <Card title="Create Entitlement" icon="plus" href="/api-reference/entitlements/create-entitlement">
    Create a Digital Files entitlement with optional external URL and instructions.
  </Card>

  <Card title="Upload File" icon="upload" href="/api-reference/entitlements/upload-file">
    Upload a file (up to 100 MB) and append it to the entitlement.
  </Card>

  <Card title="Delete File" icon="trash" href="/api-reference/entitlements/delete-file">
    Remove a file from the entitlement.
  </Card>

  <Card title="List Grants" icon="users" href="/api-reference/entitlements/list-grants">
    List grants and read the resolved download URLs.
  </Card>

  <Card title="Update Entitlement" icon="pen" href="/api-reference/entitlements/update-entitlement">
    Update instructions, external URL, or replace files.
  </Card>

  <Card title="Revoke Grant" icon="ban" href="/api-reference/entitlements/revoke-grant">
    Manually revoke a customer's access.
  </Card>
</CardGroup>

***

## Webhooks

Digital file delivery and revocation fire the four [`entitlement_grant.*` webhook events](/developer-resources/webhooks/intents/entitlement-grant). For Digital Files grants, the payload includes a `digital_product_delivery` object with the resolved file list (presigned URLs, filenames, sizes), the optional `instructions`, and the optional `external_url`.

```json theme={null} theme={null}
"digital_product_delivery": {
  "files": [
    {
      "file_id": "df_a4f6c1de",
      "download_url": "https://files.dodopayments.com/.../pro-bundle.zip?Signature=...",
      "filename": "pro-bundle.zip",
      "content_type": "application/zip",
      "file_size": 18742390,
      "expires_in": 900
    }
  ],
  "instructions": "Unzip and run setup.sh from the project root.",
  "external_url": null
}
```

***

## Legacy Digital Product Delivery

<Note>
  Products configured with the older `digital_product_delivery` block on the product itself have been **automatically migrated** to a Digital Files entitlement. Existing files uploaded under the legacy product file API are preserved; they continue to be downloadable and appear in grant payloads tagged with `source: "legacy"`. Future updates (adding files, changing instructions, replacing the external URL) should be made by editing the migrated Digital Files entitlement under **Entitlements**.

  The legacy product-level fields (`digital_product_delivery.external_url`, `digital_product_delivery.instructions`) continue to be populated on product responses for backwards compatibility, but the entitlement is the source of truth going forward.
</Note>

***

## Best Practices

* **Treat downloads as one-shot.** Customers will share or lose links, so design your product around the assumption that anything they download is theirs to keep.
* **Use instructions to set expectations.** For multi-file bundles, add an `instructions` line explaining what to install first or how to combine the files.
* **Watch the 100 MB cap.** Larger artifacts (multi-GB datasets, video courses) should be hosted externally and linked via `external_url` instead of uploaded.
* **Combine with License Keys for revocable access.** If you need to revoke access to in-product features after a refund, pair the Digital Files entitlement with a License Key entitlement and validate the key at runtime.
* **Test the customer-portal refresh flow.** Confirm a customer can return to the portal a week later and still get a working download link. This is the primary recovery path when email links expire.
