Skip to main content

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​

{
"orders": [
{
"type": "permission",
"external_id": "BULK-001",
"user": { "email": "[email protected]" },
"products": [{ "type": "content", "external_id": "EBOOK-001" }]
},
{
"type": "permission",
"external_id": "BULK-002",
"user": { "email": "[email protected]" },
"products": [{ "type": "content", "external_id": "EBOOK-001" }]
}
]
}

Request Structure​

FieldTypeDescription
ordersarrayArray of order objects (1-100 orders)

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

Limits​

LimitValue
Maximum orders per request100
Minimum orders per request1

Request Examples​

Bulk Permission Orders​

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": "[email protected]"
},
"products": [
{
"type": "content",
"external_id": "9781234567890"
}
]
},
{
"type": "permission",
"external_id": "PROMO-002",
"user": {
"id": "user-002",
"email": "[email protected]"
},
"products": [
{
"type": "content",
"external_id": "9781234567890"
}
]
},
{
"type": "permission",
"external_id": "PROMO-003",
"user": {
"id": "user-003",
"email": "[email protected]"
},
"products": [
{
"type": "content",
"external_id": "9781234567890"
}
]
}
]
}'

Bulk Orders with Different Types​

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": "[email protected]" },
"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": "[email protected]"
},
"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.

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": "[email protected]" },
"products": [
{
"type": "subscription",
"external_id": "PREMIUM-MONTHLY",
"expiration_date": "2026-01-31"
}
]
},
{
"type": "permission",
"external_id": "TRIAL-002",
"user": { "email": "[email protected]" },
"products": [
{
"type": "subscription",
"external_id": "PREMIUM-MONTHLY",
"expiration_date": "2026-01-31"
}
]
}
]
}'

Response​

Success Response (200 OK)​

{
"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​

FieldTypeDescription
totalintegerTotal number of orders in request
createdintegerNumber of successfully created orders
failedintegerNumber of failed orders
ordersarrayCreated orders with id, uuid, external_id, status
errorsarrayOnly present if there are failures
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 or List Orders endpoints after creation.


Partial Success Handling​

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

{
"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."]
}
}
]
}
}
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)​

{
"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)​

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

Common Error Causes​

ErrorCause
orders field is requiredMissing orders array
orders must be an arrayorders is not an array
orders may not have more than 100 itemsExceeded 100 order limit
orders.N.type is requiredOrder at index N missing type
orders.N.products.M.external_id does not existProduct at index M of order N not found
The expiration date must be a future dateExpiration 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:

// 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:

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:

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

4. Track Progress​

For large imports, track which batches succeeded:

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​

AspectRecommendation
Batch sizeUse 50-100 orders for optimal throughput
Retry logicImplement exponential backoff for rate limiting
Parallel requestsAvoid parallel bulk requests; process sequentially
TimeoutAllow 30+ seconds for large batches

Use Cases​

Corporate Onboarding​

Grant access to all employees at once:

{
"orders": [
{
"type": "permission",
"external_id": "CORP-EMP-001",
"user": { "email": "[email protected]" },
"products": [{ "type": "subscription", "external_id": "ENTERPRISE-PLAN" }]
},
{
"type": "permission",
"external_id": "CORP-EMP-002",
"user": { "email": "[email protected]" },
"products": [{ "type": "subscription", "external_id": "ENTERPRISE-PLAN" }]
}
]
}

Promotional Campaign​

Mass distribution of free content:

{
"orders": [
{
"type": "permission",
"external_id": "PROMO-NEWSLETTER-001",
"user": { "email": "[email protected]" },
"products": [
{
"type": "content",
"external_id": "FREE-SAMPLE-CHAPTER",
"expiration_date": "2026-02-28"
}
]
}
]
}

Migration from Legacy System​

Import existing access records:

{
"orders": [
{
"type": "permission",
"external_id": "LEGACY-12345",
"user": { "id": "legacy-user-001", "email": "[email protected]" },
"products": [
{ "type": "content", "external_id": "EBOOK-001" },
{ "type": "content", "external_id": "EBOOK-002" }
]
}
]
}

See Also​

X

Graph View