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

# Dynamic Delivery

> Automatically deliver product content using a secure webhook integration.

## Overview

Dynamic Delivery allows SellAuth to **automatically deliver product content by sending a POST request to your server**.
Your server generates and returns the deliverables in real time (for example: license keys, credentials, or access tokens).

Stock for Dynamic Delivery products is managed manually and can be set to **infinite**.

<Note>Reselling another SellAuth shop's products? That shop generates ready-made delivery URLs for you; no server needed. See [Reseller Dynamic Delivery](/resellers/dynamic-delivery).</Note>

## How It Works

1. A customer completes checkout.
2. SellAuth sends a **POST request to your webhook URL for each item in the invoice**.
3. Your server processes the request and returns the deliverables.
4. SellAuth displays the returned content to the customer.

Each product item is delivered **individually**, even if multiple items exist in the same cart or invoice.

## Webhook Request

### HTTP Details

* **Method:** `POST`
* **Content-Type:** `application/json`
* **Connect timeout:** 5 seconds
* **Request timeout:** 10 seconds
* **Retries:** 3 total attempts
* **Retry interval:** 5 seconds between each attempt
* **Maximum response size:** 1 MB

### Example Request Body

```json theme={null}
{
  "event": "INVOICE.ITEM.DELIVER-DYNAMIC",
  "id": 10042,
  "unique_id": "8e32b8f24c4a0-0000000010042",

  "status": "partially_completed",
  "status_description": null,

  "price": "25.00",
  "paid": "25.00",
  "currency": "USD",
  "amount": 1,
  "tax_rate": "0.00",
  "coupon_discount": "0.00",
  "gateway_fee": "0.00",

  "gateway": "STRIPE",
  "payment_method_id": 10,
  "stripe_pi_id": "pi_demo_9f83ksL2",

  "email": "john.doe@example.com",
  "ip": "203.0.113.42",
  "country_code": "US",
  "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0",

  "shop_id": 1,
  "shop_customer_id": 501,

  "created_at": "2026-02-01T11:30:00Z",
  "completed_at": "2026-02-01T11:31:10Z",
  "updated_at": "2026-02-01T11:31:15Z",

  "customer": {
    "id": 501,
    "shop_id": 1,
    "email": "john.doe@example.com"
  },

  "payments": [
    {
      "id": 9001,
      "invoice_id": 10042,
      "shop_id": 1,
      "status": "completed",
      "amount": "25.00",
      "currency": "USD",
      "external_id_type": "stripe_payment_intent_id",
      "external_id_value": "pi_demo_9f83ksL2",
      "created_at": "2026-02-01T11:31:05Z",
      "updated_at": "2026-02-01T11:31:05Z"
    }
  ],

  "item": {
    "id": 7001,
    "invoice_id": 10042,
    "product_id": 300,
    "variant_id": 45,
    "status": "failed",
    "price": "25.00",
    "quantity": 1,
    "tax_inclusive": true,
    "total_tax": "0.00",
    "total_price": "25.00",
    "custom_fields": {
      "Custom Field Name": "Custom Field Value"
    },
    "completed_at": "2026-02-01T11:31:15Z",

    "product": {
      "id": 300,
      "name": "Premium Digital Access",
      "type": "variant"
    },

    "variant": {
      "id": 45,
      "name": "Standard License"
    }
  }
}
```

### Request Headers

```http theme={null}
Idempotency-Key: c47107fff54fd943db6939b9935af39a7753b7dc9a3bd227de71d032a725bb47
X-Timestamp: 1769945465
X-Signature: 9c64b5dcf02dfbe918357f4bf92731f28d7ba6988e9af3dec821840367f420fb
Content-Type: application/json
```

## Signature Verification

Each request is signed using **HMAC-SHA256** and can be verified using the `X-Signature` header.

The webhook secret can be found in your dashboard under
**Storefront > Configure > Miscellaneous**: [dash.sellauth.com/shop#miscellaneous](https://dash.sellauth.com/shop#miscellaneous)

If the secret is not visible, click **Regenerate** to create one.

### Signature Generation Logic

The signature is generated from the **raw JSON body**:

```php theme={null}
hash_hmac(
  'sha256',
  json_encode($requestData),
  $webhookSecret
);
```

<Tabs>
  <Tab title="JavaScript">
    ```js theme={null}
    import crypto from 'crypto';

    const payload = JSON.stringify(req.body);

    const signature = crypto
      .createHmac('sha256', WEBHOOK_SECRET)
      .update(payload)
      .digest('hex');

    if (signature !== req.headers['x-signature']) {
      throw new Error('Invalid signature');
    }
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    $payload = file_get_contents('php://input');
    $signature = hash_hmac('sha256', $payload, $webhookSecret);

    if ($signature !== $_SERVER['HTTP_X_SIGNATURE']) {
      http_response_code(401);
      exit('Invalid signature');
    }
    ```
  </Tab>

  <Tab title="Python">
    ```py theme={null}
    import hmac
    import hashlib

    payload = request.get_data()
    signature = hmac.new(
        WEBHOOK_SECRET.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()

    if signature != request.headers.get("X-Signature"):
        raise Exception("Invalid signature")
    ```
  </Tab>
</Tabs>

## Webhook Response

### Success Response

If your server responds with **HTTP 200**, the item is marked as **completed** and the returned body is shown to the customer.

Each line in the response body is treated as a separate deliverable.

### Example Success Response

```js theme={null}
res.status(200).send(
`LICENSE-KEY-ABC-123
LICENSE-KEY-DEF-456
LICENSE-KEY-GHI-789`
);
```

### Retryable Errors

SellAuth will automatically retry the request if a network error occurs (connection timeout, request timeout, etc.) or if your server responds with:

* **429 (Too Many Requests)**
* **500 (Internal Server Error)**
* **501 (Not Implemented)**
* **502 (Bad Gateway)**
* **503 (Service Unavailable)**
* **504 (Gateway Timeout)**

### Non-Retryable Errors

If the response status is **not 200, not 429, and not any of the 5xx codes listed above**:

* The item is marked as **failed**.
* If a response body is provided, it will be displayed on the checkout page so the customer can see your error message.

### Example Error Response

```js theme={null}
res.status(400).send('We are currently out of stock, please wait for restock.');
```

## Invoice Status Behavior

* If the webhook **fails or times out**, the invoice status will be **`partially_completed`**
* If the webhook succeeds **and all items are delivered**, the invoice status will be **`completed`**
* If the webhook succeeds **but not all items are delivered**, the invoice status will remain **`partially_completed`**


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.