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

# Digital Product Delivery

> How digital products are fulfilled, and how to generate a unique code per purchase

A product marked **digital** is fulfilled automatically once payment is
confirmed — not when the order is placed. The customer receives the
[digital delivery email](/extensions/email-templates), which carries whatever
the product delivers.

<Note>
  Fulfilment runs inside the payment-success path, so nothing is delivered for
  an unpaid, pending or failed order. You do not need to build your own payment
  gate in front of it.
</Note>

## The four delivery types

| Type         | What the merchant configures | What the customer receives                      |
| ------------ | ---------------------------- | ----------------------------------------------- |
| `file`       | One or more file URLs        | Each file's name with its own download button   |
| `codes`      | A pre-loaded list of codes   | The next unused code(s), one per unit purchased |
| `dynamicurl` | An endpoint you host         | Whatever your endpoint returns for that order   |
| `service`    | Free-text details            | The text, e.g. booking instructions             |

### `codes` — a pre-loaded pool

The merchant uploads a list of codes on the product. Each paid order consumes
the next `quantity` codes from the front of the list and the pool shrinks.
Simple, but you supply the codes in advance and delivery stops when the pool
runs dry.

### `dynamicurl` — generate a code per purchase

Use this when each customer needs a code minted at the moment of purchase — a
licence key, a seat, a one-time redemption token.

On each paid order we send a `POST` to the URL configured on the product:

```
POST <your-url>?id=<product_id>
Content-Type: application/json
```

```json theme={null}
{
  "product": {
    "id": "c3f1…",
    "name": "Signalcraft",
    "quantity": 1
  },
  "order": {
    "id": "9a2b…",
    "invoice_id": "1042",
    "created_at": "2026-08-19T10:22:31.000Z",
    "currency": "EUR",
    "total": "49.00"
  },
  "customer": {
    "name": "Alex Morgan",
    "email": "alex@example.com",
    "mobile": "+3312345678"
  },
  "shop": {
    "name": "Acme Store",
    "domain": "www.acme.com"
  }
}
```

Respond with the value to deliver under a `data` key:

```json theme={null}
{ "data": "SIGC-9F3K-22QX-7B1D" }
```

That value is what the customer receives in the delivery email, and it is
stored against the order.

<Note>
  The `?id=<product_id>` query parameter is still sent for backwards
  compatibility. Existing endpoints that read only the query string keep
  working unchanged.
</Note>

**Requirements**

* Respond within **15 seconds**; the request times out after that.
* Return `{ "data": ... }`. A response without a `data` key is treated as a
  failure and nothing is delivered for that line.
* Your endpoint is a public URL — authenticate it yourself, for example with a
  secret in the query string (`https://you.example.com/mint?secret=…`). We
  preserve any query string you configure.
* Expect retries on transient failure, so make issuance idempotent per
  `order.id` + `product.id`.

## Building a protected post-purchase experience

Delivery is where the platform's responsibility ends. Validating a code,
enforcing single use, rate-limiting attempts, and revoking access are yours to
build. The supported shape:

<Steps>
  <Step title="Mint the code">
    Configure the product as `dynamicurl` pointing at your endpoint. Store the
    returned code against `order.id` and `customer.email` in your own database.
  </Step>

  <Step title="Confirm the sale">
    Subscribe to the `orders/paid` webhook so you have an authoritative record
    of the paid order, independent of the fulfilment call.
  </Step>

  <Step title="Serve the protected page">
    Host the gated content behind an [App Proxy](/extensions/app-proxy). It
    appears on the merchant's own domain and each request arrives signed, so you
    can trust its origin while your server performs the code check.
  </Step>

  <Step title="Revoke on refund">
    Subscribe to `refunds/create` and `orders/cancelled`, and mark the
    associated code invalid when either fires. See
    [webhook topics](/api-reference/webhooks/topics).
  </Step>
</Steps>

<Warning>
  There is no built-in code-redemption endpoint, no automatic revocation, and
  no anti-sharing or brute-force protection. If your product needs those, they
  belong in the backend behind your App Proxy.
</Warning>

## Customising the delivery email

The email is merchant-editable under **Store Settings → Email templates →
Digital delivery**. Use `{{ delivery_html }}` to place the delivery itself —
it renders the right component for the product type and is already built to
display correctly in Outlook. See
[Email templates](/extensions/email-templates#variables-available-for-digital_delivery)
for the full variable list.
