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

# Bulk Operations

Create multiple orders in a single API call. The bulk endpoint processes orders concurrently, making it significantly faster than sequential individual requests.

## When to Use Bulk Operations

- **Batch imports**: Migrating orders from another system
- **Corporate provisioning**: Granting access to multiple users at once
- **Promotional campaigns**: Mass distribution of promotional access
- **Subscription renewals**: Processing multiple renewals simultaneously

---

## Creating Orders in Bulk

### Endpoint

```
POST /api/v3/orders/bulk
```

### Request Body

```json
{
  "orders": [
    {
      "type": "permission",
      "external_id": "BULK-001",
      "user": { "email": "user1@example.com" },
      "products": [{ "type": "content", "external_id": "EBOOK-001" }]
    },
    {
      "type": "permission",
      "external_id": "BULK-002",
      "user": { "email": "user2@example.com" },
      "products": [{ "type": "content", "external_id": "EBOOK-001" }]
    }
  ]
}
```

### Request Structure

| Field    | Type  | Description                           |
| -------- | ----- | ------------------------------------- |
| `orders` | array | Array of order objects (1-100 orders) |

Each order object follows the same structure as individual order creation. See:

- [Permission Orders](./orders-v3-permission.md)
- [Report Orders](./orders-v3-report.md)
- [Sale Orders](./orders-v3-sale.md)

### Limits

| Limit                      | Value |
| -------------------------- | ----- |
| Maximum orders per request | 100   |
| Minimum orders per request | 1     |

---

## Request Examples

### Bulk Permission Orders

```bash
curl -X POST "https://yourstore.publica.la/api/v3/orders/bulk" \
  -H "X-User-Token: your-api-token" \
  -H "Content-Type: application/json" \
  -d '{
    "orders": [
      {
        "type": "permission",
        "external_id": "PROMO-001",
        "user": {
          "id": "user-001",
          "email": "user1@example.com"
        },
        "products": [
          {
            "type": "content",
            "external_id": "9781234567890"
          }
        ]
      },
      {
        "type": "permission",
        "external_id": "PROMO-002",
        "user": {
          "id": "user-002",
          "email": "user2@example.com"
        },
        "products": [
          {
            "type": "content",
            "external_id": "9781234567890"
          }
        ]
      },
      {
        "type": "permission",
        "external_id": "PROMO-003",
        "user": {
          "id": "user-003",
          "email": "user3@example.com"
        },
        "products": [
          {
            "type": "content",
            "external_id": "9781234567890"
          }
        ]
      }
    ]
  }'
```

### Bulk Orders with Different Types

```bash
curl -X POST "https://yourstore.publica.la/api/v3/orders/bulk" \
  -H "X-User-Token: your-api-token" \
  -H "Content-Type: application/json" \
  -d '{
    "orders": [
      {
        "type": "permission",
        "external_id": "FREE-ACCESS-001",
        "user": { "email": "vip@example.com" },
        "products": [
          { "type": "subscription", "external_id": "PREMIUM-PLAN" }
        ]
      },
      {
        "type": "report",
        "external_id": "PARTNER-SALE-001",
        "return_url": "https://partner.com/receipt",
        "unit_price": 29.99,
        "currency_id": "USD",
        "user": {
          "id": "partner-customer-001",
          "email": "customer@partner.com"
        },
        "products": [
          {
            "type": "content",
            "external_id": "EBOOK-001",
            "unit_price": 29.99,
            "currency_id": "USD"
          }
        ]
      }
    ]
  }'
```

### Bulk Orders with Expiration

Every `expiration_date` must be later than the current day in your store's timezone. The API checks every date before it creates any order, so a single invalid date rejects the whole request with `422` instead of returning a per-order error.

```bash
curl -X POST "https://yourstore.publica.la/api/v3/orders/bulk" \
  -H "X-User-Token: your-api-token" \
  -H "Content-Type: application/json" \
  -d '{
    "orders": [
      {
        "type": "permission",
        "external_id": "TRIAL-001",
        "user": { "email": "trial1@example.com" },
        "products": [
          {
            "type": "subscription",
            "external_id": "PREMIUM-MONTHLY",
            "expiration_date": "2026-01-31"
          }
        ]
      },
      {
        "type": "permission",
        "external_id": "TRIAL-002",
        "user": { "email": "trial2@example.com" },
        "products": [
          {
            "type": "subscription",
            "external_id": "PREMIUM-MONTHLY",
            "expiration_date": "2026-01-31"
          }
        ]
      }
    ]
  }'
```

---

## Response

### Success Response (200 OK)

```json
{
  "data": {
    "total": 3,
    "created": 3,
    "failed": 0,
    "orders": [
      {
        "id": "12345",
        "uuid": "275ca6c4-a815-4f64-8198-759a296dd495",
        "external_id": "PROMO-001",
        "status": "approved"
      },
      {
        "id": "12346",
        "uuid": "385da7d5-b926-5g75-9309-86a307dd596",
        "external_id": "PROMO-002",
        "status": "approved"
      },
      {
        "id": "12347",
        "uuid": "495eb8e6-ca37-6h86-0410-97b418ee6a7",
        "external_id": "PROMO-003",
        "status": "approved"
      }
    ]
  }
}
```

### Response Fields

| Field     | Type    | Description                                               |
| --------- | ------- | --------------------------------------------------------- |
| `total`   | integer | Total number of orders in request                         |
| `created` | integer | Number of successfully created orders                     |
| `failed`  | integer | Number of failed orders                                   |
| `orders`  | array   | Created orders with `id`, `uuid`, `external_id`, `status` |
| `errors`  | array   | Only present if there are failures                        |

:::tip Retrieving Full Order Details
The bulk response returns minimal order data for performance. To get full order details including `user` and `products`, use the [Get Order](../orders-v3-get.md) or [List Orders](../orders-v3-list.md) endpoints after creation.
:::

---

## Partial Success Handling

Bulk operations support partial success. Some orders may succeed while others fail:

```json
{
  "data": {
    "total": 3,
    "created": 2,
    "failed": 1,
    "orders": [
      {
        "id": "12345",
        "uuid": "275ca6c4-a815-4f64-8198-759a296dd495",
        "external_id": "ORDER-001",
        "status": "approved"
      },
      {
        "id": "12346",
        "uuid": "385da7d5-b926-5g75-9309-86a307dd596",
        "external_id": "ORDER-003",
        "status": "approved"
      }
    ],
    "errors": [
      {
        "index": 1,
        "external_id": "ORDER-002",
        "errors": {
          "products.0.external_id": ["The product does not exist."]
        }
      }
    ]
  }
}
```

:::info Partial Success
Successfully validated orders are created even if other orders in the batch fail. Check the `failed` count and `errors` array to identify and retry failed orders. Request validation runs first, so a payload error such as an expiration date that is not in the future rejects every order in the request.
:::

---

## Error Handling

### Validation Errors (422)

```json
{
  "message": "The given data was invalid.",
  "errors": {
    "orders": ["The orders field is required."],
    "orders.0.type": ["The orders.0.type field is required."],
    "orders.1.products.0.external_id": ["The product does not exist."],
    "orders.2.products.0.expiration_date": [
      "The expiration date must be a future date"
    ]
  }
}
```

### Too Many Orders (422)

```json
{
  "message": "The given data was invalid.",
  "errors": {
    "orders": ["The orders may not have more than 100 items."]
  }
}
```

### Common Error Causes

| Error                                            | Cause                                                     |
| ------------------------------------------------ | --------------------------------------------------------- |
| `orders field is required`                       | Missing `orders` array                                    |
| `orders must be an array`                        | `orders` is not an array                                  |
| `orders may not have more than 100 items`        | Exceeded 100 order limit                                  |
| `orders.N.type is required`                      | Order at index N missing type                             |
| `orders.N.products.M.external_id does not exist` | Product at index M of order N not found                   |
| `The expiration date must be a future date`      | Expiration date at index M of order N is today or earlier |

---

## Best Practices

### 1. Validate Before Submitting

Pre-validate your orders client-side to avoid partial failures:

```javascript
// Example: validate all products exist before bulk submission
const productIds = orders.flatMap((o) => o.products.map((p) => p.external_id));
const validProducts = await validateProducts(productIds);
if (!validProducts.allExist) {
  throw new Error('Invalid product IDs: ' + validProducts.missing.join(', '));
}
```

### 2. Batch Large Imports

For more than 100 orders, split into batches:

```javascript
const BATCH_SIZE = 100;
const batches = chunk(orders, BATCH_SIZE);

for (const batch of batches) {
  await createBulkOrders(batch);
}
```

### 3. Use Unique External References

Ensure each order has a unique `external_id`:

```javascript
orders.forEach((order, index) => {
  order.external_id = `IMPORT-${Date.now()}-${index}`;
});
```

### 4. Track Progress

For large imports, track which batches succeeded:

```javascript
const results = [];
for (let i = 0; i < batches.length; i++) {
  try {
    const result = await createBulkOrders(batches[i]);
    results.push({ batch: i, success: true, data: result });
  } catch (error) {
    results.push({ batch: i, success: false, error: error.message });
  }
}
```

---

## Performance Considerations

| Aspect            | Recommendation                                     |
| ----------------- | -------------------------------------------------- |
| Batch size        | Use 50-100 orders for optimal throughput           |
| Retry logic       | Implement exponential backoff for rate limiting    |
| Parallel requests | Avoid parallel bulk requests; process sequentially |
| Timeout           | Allow 30+ seconds for large batches                |

---

## Use Cases

### Corporate Onboarding

Grant access to all employees at once:

```json
{
  "orders": [
    {
      "type": "permission",
      "external_id": "CORP-EMP-001",
      "user": { "email": "employee1@company.com" },
      "products": [{ "type": "subscription", "external_id": "ENTERPRISE-PLAN" }]
    },
    {
      "type": "permission",
      "external_id": "CORP-EMP-002",
      "user": { "email": "employee2@company.com" },
      "products": [{ "type": "subscription", "external_id": "ENTERPRISE-PLAN" }]
    }
  ]
}
```

### Promotional Campaign

Mass distribution of free content:

```json
{
  "orders": [
    {
      "type": "permission",
      "external_id": "PROMO-NEWSLETTER-001",
      "user": { "email": "subscriber1@example.com" },
      "products": [
        {
          "type": "content",
          "external_id": "FREE-SAMPLE-CHAPTER",
          "expiration_date": "2026-02-28"
        }
      ]
    }
  ]
}
```

### Migration from Legacy System

Import existing access records:

```json
{
  "orders": [
    {
      "type": "permission",
      "external_id": "LEGACY-12345",
      "user": { "id": "legacy-user-001", "email": "user@legacy.com" },
      "products": [
        { "type": "content", "external_id": "EBOOK-001" },
        { "type": "content", "external_id": "EBOOK-002" }
      ]
    }
  ]
}
```

---

## See Also

- [Permission Orders](./orders-v3-permission.md) - Individual permission order creation
- [Report Orders](./orders-v3-report.md) - Individual report order creation
- [List Orders](../orders-v3-list.md) - Query created orders
