# Orders

> 💡 **Bulk upsert orders with items for a tenant**

***

## 🚀 TL;DR - Quick Reference

**Endpoint**: `POST /orders/{tenantId}`

**Use When**:

* Customer completes a purchase
* Importing historical order data
* Order refund/cancellation
* Syncing e-commerce transactions

**Required**: `OrderId`, `Currency`\
**Auth**: `x-replenit-auth-key` header\
**Returns**: Count of upserted orders

**Quickest Example**:

```bash
POST /orders/{YOUR_TENANT_ID}
[{
  "OrderId": "O-789",
  "Currency": "USD",
  "TotalRevenue": 99.99,
  "OrderItems": []
}]
```

[Jump to Full API Reference ↓](#endpoint)

***

## 🔗 Endpoint

```http
POST /orders/{tenantId}
```

### 📋 Parameters

| Parameter  | Type   | Required | Description                        |
| ---------- | ------ | -------- | ---------------------------------- |
| `tenantId` | `GUID` | ✅ Yes    | The tenant identifier (must exist) |

### 📨 Request

* **Body**: JSON array of `UpsertOrderDto` objects
* **Content-Type**: `application/json`

### ✅ Success Response

* **Status**: `200 OK`
* **Body**: Response envelope with `data.count`

### ❌ Error Responses

| Status                      | Description                          |
| --------------------------- | ------------------------------------ |
| `400 Bad Request`           | Invalid or missing tenant identifier |
| `404 Not Found`             | Tenant does not exist in our system  |
| `500 Internal Server Error` | Unexpected server errors             |

***

## 🎯 Identifier Behavior

### Customer Identification

| Priority Setting  | Identifier Used          |
| ----------------- | ------------------------ |
| `customerid`      | Customer ID from order   |
| `email` (default) | Email address from order |

### Product Identification

> 📝 **Note**: Order items currently use `ProductId` for identification

***

## 📄 Request Schema

### UpsertOrderDto Fields

<table><thead><tr><th>Field</th><th>Type</th><th>Max Length</th><th>Default</th><th width="109.23828125">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>OrderId</code></td><td><code>string</code></td><td>100</td><td>-</td><td>✅</td><td>Unique order identifier</td></tr><tr><td><code>Email</code></td><td><code>string</code></td><td>-</td><td>-</td><td>❌</td><td>Customer email (optional if CustomerId is sent)</td></tr><tr><td><code>CustomerId</code></td><td><code>string</code></td><td>-</td><td>-</td><td>❌</td><td>Customer ID (used if priority is "CustomerId")</td></tr><tr><td><code>TotalQuantity</code></td><td><code>int</code></td><td>-</td><td>1</td><td>❌</td><td>Total items in order</td></tr><tr><td><code>TotalRevenue</code></td><td><code>double</code></td><td>-</td><td>1</td><td>❌</td><td>Total order value</td></tr><tr><td><code>OrderDate</code></td><td><code>string</code></td><td>-</td><td>UTC now</td><td>❌</td><td>ISO-8601 datetime; defaults to server UTC time if omitted</td></tr><tr><td><code>Currency</code></td><td><code>string</code></td><td>3</td><td>USD</td><td>✅</td><td>Currency code (ISO 4217)</td></tr><tr><td><code>UniqueQuantity</code></td><td><code>int</code></td><td>-</td><td>1</td><td>❌</td><td>Number of unique items</td></tr><tr><td><code>IsOrderCancelled</code></td><td><code>bool</code></td><td>-</td><td>false</td><td>❌</td><td>Cancellation status</td></tr><tr><td><code>OrderItems</code></td><td><code>CreateOrderItemDto[]</code></td><td>-</td><td>[]</td><td>❌</td><td>Order line items</td></tr></tbody></table>

### CreateOrderItemDto Fields

| Field           | Type     | Max Length | Required | Description        |
| --------------- | -------- | ---------- | -------- | ------------------ |
| `ProductId`     | `string` | 100        | ✅        | Product identifier |
| `Sku`           | `string` | 50         | ✅        | Product SKU        |
| `Quantity`      | `int`    | -          | ✅        | Item quantity      |
| `Price`         | `double` | -          | ✅        | Item price         |
| `Size`          | `string` | 50         | ❌        | Size (e.g., "1l")  |
| `Color`         | `string` | 50         | ❌        | Color variant      |
| `Brand`         | `string` | -          | ❌        | Brand name         |
| `IsGift`        | `bool`   | -          | ❌        | Gift item flag     |
| `OriginalPrice` | `double` | -          | ❌        | Pre-discount price |

> 💡 **Note**: Size is automatically parsed into `SizeNumeric` and `SizeMetric` on the server

***

## 🗑️ DELETE Endpoints

Order deletions cover two scopes:

* **Entire order** — removes the order and all of its line items.
* **Single line item** — removes one line item from an existing order.

Both scopes are queued for downstream processing and applied idempotently. Endpoints return success once the request is durably accepted, regardless of whether a matching record exists.

| Scope        | Endpoint                                                    |
| ------------ | ----------------------------------------------------------- |
| Entire order | `DELETE /orders/{tenantId}/{orderId}`                       |
| Line item    | `DELETE /orders/{tenantId}/{orderId}/items?productId=&sku=` |

### 🔗 Delete Order

```http
DELETE /orders/{tenantId}/{orderId}
```

#### 📋 Parameters

| Parameter  | Type     | Required     | Description            |
| ---------- | -------- | ------------ | ---------------------- |
| `tenantId` | `GUID`   | ✅ Yes (path) | The tenant identifier  |
| `orderId`  | `string` | ✅ Yes (path) | The order ID to delete |

#### ✅ Success Response

* **Status**: `200 OK`

```json
{
  "success": true,
  "message": "Order removed.",
  "data": {
    "orderId": "O-789",
    "deletedAt": "2024-12-22T14:02:49Z"
  }
}
```

#### ❌ Error Responses

| Status | Scenario         | Example Response                                                       |
| ------ | ---------------- | ---------------------------------------------------------------------- |
| `400`  | Missing order ID | `{"success": false, "message": "Order ID is required.", "data": {}}`   |
| `404`  | Tenant not found | `{"success": false, "message": "Tenant not found.", "data": {}}`       |
| `500`  | Server error     | `{"success": false, "message": "Failed to delete order.", "data": {}}` |

### 🔗 Delete Line Item

```http
DELETE /orders/{tenantId}/{orderId}/items?productId={productId}&sku={sku}
```

A line item is identified by `(orderId, productId or sku)`. Send both identifiers when available — downstream resolution may prioritize one over the other depending on tenant configuration.

#### 📋 Parameters

At least one of `productId` or `sku` must be provided.

| Parameter   | Type     | Required       | Description                               |
| ----------- | -------- | -------------- | ----------------------------------------- |
| `tenantId`  | `GUID`   | ✅ Yes (path)   | The tenant identifier                     |
| `orderId`   | `string` | ✅ Yes (path)   | The order containing the line item        |
| `productId` | `string` | ⚠️ Conditional | Tenant-scoped product ID of the line item |
| `sku`       | `string` | ⚠️ Conditional | SKU of the line item                      |

#### ✅ Success Response

```json
{
  "success": true,
  "message": "Order item removed.",
  "data": {
    "orderId": "O-789",
    "productId": "P-42",
    "sku": "SKU-001",
    "deletedAt": "2024-12-22T14:02:49Z"
  }
}
```

#### ❌ Error Responses

| Status | Scenario                           | Example Response                                                              |
| ------ | ---------------------------------- | ----------------------------------------------------------------------------- |
| `400`  | Missing order ID                   | `{"success": false, "message": "Order ID is required.", "data": {}}`          |
| `400`  | Neither productId nor sku provided | `{"success": false, "message": "Product ID or SKU is required.", "data": {}}` |
| `404`  | Tenant not found                   | `{"success": false, "message": "Tenant not found.", "data": {}}`              |
| `500`  | Server error                       | `{"success": false, "message": "Failed to delete order item.", "data": {}}`   |

**Note**: Tenant validation is performed automatically by middleware before reaching the endpoint.

### 🚀 DELETE Examples

#### cURL — entire order

```bash
curl -X DELETE "https://api.replen.it/orders/{tenantId}/O-789" \
  -H 'x-replenit-auth-key: YOUR_BASE64_ACCESS_KEY'
```

#### cURL — single line item

```bash
curl -X DELETE "https://api.replen.it/orders/{tenantId}/O-789/items?productId=P-42&sku=SKU-001" \
  -H 'x-replenit-auth-key: YOUR_BASE64_ACCESS_KEY'
```

#### Python

```python
import requests

base = f"https://api.replen.it/orders/{tenant_id}"
headers = {"x-replenit-auth-key": "YOUR_BASE64_ACCESS_KEY"}

# Delete entire order
requests.delete(f"{base}/O-789", headers=headers)

# Delete a single line item
requests.delete(f"{base}/O-789/items", headers=headers, params={
    "productId": "P-42",
    "sku": "SKU-001",
})
```

#### Node.js

```javascript
const axios = require('axios');

const base = `https://api.replen.it/orders/${tenantId}`;
const headers = { 'x-replenit-auth-key': 'YOUR_BASE64_ACCESS_KEY' };

await axios.delete(`${base}/O-789`, { headers });
await axios.delete(`${base}/O-789/items`, {
    headers,
    params: { productId: 'P-42', sku: 'SKU-001' },
});
```

***

## 💡 Real-World Use Cases

### Use Case 1: E-commerce Purchase

**Scenario**: Customer just purchased 2 items from your online store

**What to send**:

```json
[{
  "OrderId": "SHOP-2024-789",
  "Email": "customer@example.com",
  "CustomerId": "C-123",
  "TotalQuantity": 3,
  "TotalRevenue": 149.97,
  "Currency": "USD",
  "OrderDate": "2024-12-22T10:30:00Z",
  "OrderItems": [
    {
      "ProductId": "P-42",
      "Sku": "SKU-42-L",
      "Quantity": 2,
      "Price": 49.99,
      "Size": "L"
    },
    {
      "ProductId": "P-43",
      "Sku": "SKU-43-M",
      "Quantity": 1,
      "Price": 49.99,
      "Size": "M"
    }
  ]
}]
```

**What happens**:

1. ✅ Order synced to customer profile
2. 🎯 Triggers "post-purchase" automation
3. 📧 Thank you email sent
4. 📊 Revenue attributed to customer

***

### Use Case 2: Order Refund

**Scenario**: Customer returned an order, mark it as cancelled

**What to send**:

```json
[{
  "OrderId": "O-789",
  "IsOrderCancelled": true,
  "TotalRevenue": -99.99,
  "Currency": "USD"
}]
```

**What happens**:

1. ✅ Order marked as cancelled
2. 📊 Revenue metrics updated (subtracted)
3. 🎯 Customer removed from post-purchase flow

***

### Use Case 3: Subscription Order

**Scenario**: Recurring monthly subscription payment

**What to send**:

```json
[{
  "OrderId": "SUB-2024-12-001",
  "CustomerId": "C-456",
  "TotalRevenue": 29.99,
  "Currency": "EUR",
  "OrderDate": "2024-12-01T00:00:00Z",
  "OrderItems": [{
    "ProductId": "SUBSCRIPTION-PREMIUM",
    "Sku": "SUB-PREMIUM-MONTHLY",
    "Quantity": 1,
    "Price": 29.99
  }]
}]
```

**Best practice**: Use consistent `OrderId` pattern for subscriptions (e.g., `SUB-{YYYY-MM}-{ID}`)

***

### Use Case 4: Gift Order

**Scenario**: Customer bought items as gifts

**What to send**:

```json
[{
  "OrderId": "GIFT-2024-999",
  "Email": "buyer@example.com",
  "TotalRevenue": 150.00,
  "Currency": "GBP",
  "OrderItems": [
    {
      "ProductId": "P-100",
      "Sku": "SKU-100",
      "Quantity": 3,
      "Price": 50.00,
      "IsGift": true
    }
  ]
}]
```

**What happens**:

1. ✅ Order tracked as gift purchase
2. 🎁 Analytics show gift behavior
3. 🎯 Can trigger gift-specific campaigns

***

### 📝 Complete Example Request (All Fields)

```json
[
  {
    "OrderId": "O-789",
    "Email": "john.doe@example.com",
    "CustomerId": "C-123",
    "TotalQuantity": 3,
    "TotalRevenue": 89.97,
    "OrderDate": "2025-08-22T21:00:00Z",
    "Currency": "USD",
    "UniqueQuantity": 2,
    "IsOrderCancelled": false,
    "OrderItems": [
      {
        "ProductId": "P-42",
        "Sku": "SKU-42-RED-L",
        "Quantity": 2,
        "Price": 29.99,
        "Size": "1l",
        "Color": "red",
        "Brand": "Acme Corp",
        "IsGift": false,
        "OriginalPrice": 39.99
      },
      {
        "ProductId": "P-43",
        "Sku": "SKU-43-BLUE-M",
        "Quantity": 1,
        "Price": 29.99,
        "Size": "500ml",
        "Color": "blue",
        "Brand": "Acme Corp",
        "IsGift": true,
        "OriginalPrice": 29.99
      }
    ]
  },
  {
    "OrderId": "O-790",
    "Email": "jane.smith@example.com",
    "CustomerId": "C-124",
    "TotalQuantity": 5,
    "TotalRevenue": 149.95,
    "OrderDate": "2025-08-23T10:30:00Z",
    "Currency": "EUR",
    "UniqueQuantity": 3,
    "IsOrderCancelled": false,
    "OrderItems": [
      {
        "ProductId": "P-100",
        "Sku": "SKU-100",
        "Quantity": 3,
        "Price": 29.99,
        "Size": "XL",
        "Color": "black",
        "Brand": "Premium Brand",
        "IsGift": false,
        "OriginalPrice": 49.99
      },
      {
        "ProductId": "P-101",
        "Sku": "SKU-101",
        "Quantity": 1,
        "Price": 39.99,
        "Size": "M",
        "Color": "white",
        "Brand": "Premium Brand",
        "IsGift": false,
        "OriginalPrice": 39.99
      },
      {
        "ProductId": "P-102",
        "Sku": "SKU-102",
        "Quantity": 1,
        "Price": 19.99,
        "Size": "S",
        "Color": "green",
        "Brand": "Eco Brand",
        "IsGift": false,
        "OriginalPrice": 24.99
      }
    ]
  }
]
```

### 📝 Minimal Example Request (Required Fields Only)

```json
[
  {
    "OrderId": "O-791",
    "Currency": "USD"
  }
]
```

### 📝 Cancelled Order Example

```json
[
  {
    "OrderId": "O-792",
    "Email": "cancelled@example.com",
    "TotalQuantity": 1,
    "TotalRevenue": 49.99,
    "OrderDate": "2025-08-23T08:00:00Z",
    "Currency": "GBP",
    "UniqueQuantity": 1,
    "IsOrderCancelled": true,
    "OrderItems": [
      {
        "ProductId": "P-200",
        "Sku": "SKU-200",
        "Quantity": 1,
        "Price": 49.99
      }
    ]
  }
]
```

### 🚀 cURL Example

```bash
curl -sS \
  -X POST "https://api.replen.it/orders/{tenantId}" \
  -H 'Content-Type: application/json' \
  -H 'x-replenit-auth-key: YOUR_BASE64_ACCESS_KEY' \
  -d @orders.json
```

### 🐍 Python Example

```python
import requests
from datetime import datetime

url = "https://api.replen.it/orders/{tenantId}"
headers = {
    "Content-Type": "application/json",
    "x-replenit-auth-key": "YOUR_BASE64_ACCESS_KEY"
}

orders = [
    {
        "OrderId": "O-789",
        "Email": "john.doe@example.com",
        "CustomerId": "C-123",
        "TotalQuantity": 2,
        "TotalRevenue": 29.99,
        "OrderDate": datetime.utcnow().isoformat() + "Z",
        "Currency": "USD",
        "OrderItems": [
            {
                "ProductId": "P-42",
                "Sku": "SKU-42",
                "Quantity": 1,
                "Price": 14.99,
                "Size": "1l",
                "Color": "red",
                "Brand": "Acme"
            }
        ]
    }
]

response = requests.post(url, headers=headers, json=orders)
print(response.json())
```

### 🟢 Node.js Example

```javascript
const axios = require('axios');

const url = 'https://api.replen.it/orders/{tenantId}';
const headers = {
    'Content-Type': 'application/json',
    'x-replenit-auth-key': 'YOUR_BASE64_ACCESS_KEY'
};

const orders = [
    {
        OrderId: 'O-789',
        Email: 'john.doe@example.com',
        CustomerId: 'C-123',
        TotalQuantity: 2,
        TotalRevenue: 29.99,
        OrderDate: new Date().toISOString(),
        Currency: 'USD',
        OrderItems: [
            {
                ProductId: 'P-42',
                Sku: 'SKU-42',
                Quantity: 1,
                Price: 14.99,
                Size: '1l',
                Color: 'red',
                Brand: 'Acme'
            }
        ]
    }
];

axios.post(url, orders, { headers })
    .then(response => console.log(response.data))
    .catch(error => console.error(error));
```

***

## 📊 Response Examples

### ✅ Success Response (200)

```json
{
  "success": true,
  "message": "Orders saved.",
  "data": {
    "count": 2,
    "processedAt": "2024-12-22T14:05:51Z"
  }
}
```

### ❌ Validation Error (400) - Missing Required Fields

```json
{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "00-xyz789...",
  "errors": {
    "[0].OrderId": [
      "The OrderId field is required."
    ],
    "[0].Currency": [
      "The Currency field is required."
    ],
    "[0].OrderItems[0].ProductId": [
      "The ProductId field is required."
    ],
    "[0].OrderItems[0].Sku": [
      "The Sku field is required."
    ]
  }
}
```

**📖 What this means:**

* Your first order (index `[0]`) is missing required fields
* `OrderId` and `Currency` are required for all orders
* Each order item needs `ProductId` and `Sku`

**✅ How to fix:**

```json
[
  {
    "OrderId": "O-789",
    "Currency": "USD",
    "OrderItems": [
      {
        "ProductId": "P-42",
        "Sku": "SKU-42",
        "Quantity": 1,
        "Price": 29.99
      }
    ]
  }
]
```

**💡 Required fields:**

* **Order level**: `OrderId`, `Currency`
* **Order item level**: `ProductId`, `Sku`, `Quantity`, `Price`

### ❌ Validation Error (400) - String Length Exceeded

```json
{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "00-mno456...",
  "errors": {
    "[0].OrderId": [
      "The field OrderId must be a string with a maximum length of 100."
    ],
    "[0].Currency": [
      "The field Currency must be a string with a maximum length of 3."
    ]
  }
}
```

**📖 What this means:**

* Field values exceed maximum allowed length
* `OrderId`: maximum 100 characters (shorten your order IDs)
* `Currency`: maximum 3 characters (use standard 3-letter codes)

**✅ How to fix:**

```json
{
  "OrderId": "ORD-2024-12345",     // ✓ Under 666 chars
  "Currency": "USD"                 // ✓ Standard 3-letter code
}
```

**💡 Field length limits:**

| Field              | Max Length |
| ------------------ | ---------- |
| OrderId, ProductId | 100        |
| Sku, Size, Color   | 50         |
| Currency           | 3          |

### ❌ Not Found (404)

```json
{
  "success": false,
  "message": "Tenant not found.",
  "data": {}
}
```

### ❌ Internal Server Error (500)

```json
{
  "success": false,
  "message": "An unexpected error occurred. Please try again.",
  "data": {}
}
```

***

## 💡 Best Practices

1. **Order IDs**: Use unique, sequential order identifiers
2. **Timestamps**: Always use UTC timezone for `OrderDate` in ISO-8601 format
3. **Currency Format**: Use ISO 4217 currency codes:
   * Examples: `USD`, `EUR`, `GBP`, `JPY`, `AUD`, `CAD`, `CHF`
   * Always use 3-letter codes (not symbols like $ or €)
   * Reference: [ISO 4217 Currency Codes](https://www.iso.org/iso-4217-currency-codes.html)
4. **Calculations**: Ensure `TotalRevenue` matches sum of item prices × quantities
5. **Customer Data**: Include either Email or CustomerId based on your identifier priority
6. **Order Items**: Always include the parent `OrderId` in each item
7. **ProductIdentifiers**: Please ensure that product identifiers (`ProductId`, `SKU`) are matched with your Product Catalog, so each event can be accurately linked to the correct product details (e.g., name, image, price)
8. **Batch Size**: Recommended batch size is 50-200 orders per request
9. **Error Handling**: Implement retry logic with exponential backoff for 500 errors

***

## 📈 Common Use Cases

### 💳 Processing a Simple Order

```json
{
  "OrderId": "O-12345",
  "Email": "customer@example.com",
  "TotalQuantity": 1,
  "TotalRevenue": 9.99,
  "OrderDate": "2025-08-23T10:30:00Z",
  "Currency": "USD",
  "OrderItems": [
    {
      "ProductId": "P-100",
      "Sku": "SKU-100",
      "Quantity": 1,
      "Price": 9.99
    }
  ]
}
```

### 🎁 Order with Gift Items

```json
{
  "OrderId": "O-GIFT-789",
  "CustomerId": "C-456",
  "TotalQuantity": 3,
  "TotalRevenue": 75.00,
  "OrderDate": "2025-08-23T14:45:00Z",
  "Currency": "EUR",
  "OrderItems": [
    {
      "ProductId": "P-200",
      "Sku": "SKU-200",
      "Quantity": 2,
      "Price": 30.00,
      "IsGift": false
    },
    {
      "ProductId": "P-201",
      "Sku": "SKU-201",
      "Quantity": 1,
      "Price": 15.00,
      "IsGift": true
    }
  ]
}
```

***

## 🔍 Related Resources

* [Customers API Documentation](/replenit-docs/customers.md)
* [Products API Documentation](/replenit-docs/products.md)
* [Error Responses Guide](/replenit-docs/error-responses.md)
* [Best Practices Guide](/replenit-docs/best-practices.md)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://replenit.gitbook.io/replenit-docs/orders.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
